vibecodingmachine-cli 2025.12.6-1702 ā 2025.12.22-2230
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 +209 -8
- package/package.json +2 -2
- package/reproduce_issue.js +160 -0
- package/src/commands/auth.js +0 -1
- package/src/commands/auto-direct.js +55 -40
- package/src/commands/auto.js +154 -57
- package/src/commands/computers.js +4 -4
- package/src/commands/repo.js +0 -1
- package/src/commands/requirements-remote.js +10 -6
- package/src/commands/requirements.js +29 -3
- package/src/commands/status.js +0 -1
- package/src/commands/sync.js +4 -4
- 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 +185 -68
- package/src/utils/interactive.js +259 -263
- package/src/utils/kiro-installer.js +56 -24
- package/src/utils/persistent-header.js +1 -3
- package/src/utils/provider-registry.js +5 -4
- 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 {
|
|
@@ -19,6 +19,7 @@ const {
|
|
|
19
19
|
requirementsExists,
|
|
20
20
|
isComputerNameEnabled
|
|
21
21
|
} = require('vibecodingmachine-core');
|
|
22
|
+
const pkg = require('../../package.json');
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Format IDE name for display
|
|
@@ -213,95 +214,9 @@ function formatPath(fullPath) {
|
|
|
213
214
|
|
|
214
215
|
async function countRequirements() {
|
|
215
216
|
try {
|
|
216
|
-
const {
|
|
217
|
+
const { getProjectRequirementStats } = require('vibecodingmachine-core');
|
|
217
218
|
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 };
|
|
219
|
+
return await getProjectRequirementStats(repoPath);
|
|
305
220
|
} catch (error) {
|
|
306
221
|
return null;
|
|
307
222
|
}
|
|
@@ -357,25 +272,14 @@ async function showWelcomeScreen() {
|
|
|
357
272
|
|
|
358
273
|
const repoPath = process.cwd(); // Always use current working directory
|
|
359
274
|
const autoStatus = await checkAutoModeStatus();
|
|
360
|
-
const allnightStatus = await checkVibeCodingMachineExists();
|
|
361
275
|
const hostname = getHostname();
|
|
362
|
-
const requirementsFilename = await getRequirementsFilename();
|
|
363
|
-
const useHostname = await isComputerNameEnabled();
|
|
364
276
|
|
|
365
277
|
// Get current IDE from config
|
|
366
278
|
const { getAutoConfig } = require('./config');
|
|
367
279
|
const autoConfig = await getAutoConfig();
|
|
368
|
-
const currentIDE = autoConfig.ide || autoStatus.ide || 'claude-code';
|
|
369
280
|
|
|
370
281
|
// Check for requirements file
|
|
371
282
|
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
283
|
|
|
380
284
|
// Count requirements if file exists
|
|
381
285
|
const counts = hasRequirements ? await countRequirements() : null;
|
|
@@ -385,7 +289,6 @@ async function showWelcomeScreen() {
|
|
|
385
289
|
console.clear();
|
|
386
290
|
|
|
387
291
|
// Get version from package.json
|
|
388
|
-
const pkg = require('../../package.json');
|
|
389
292
|
const version = `v${pkg.version}`;
|
|
390
293
|
|
|
391
294
|
// Display welcome banner with version
|
|
@@ -485,7 +388,7 @@ async function showRequirementsTree() {
|
|
|
485
388
|
};
|
|
486
389
|
|
|
487
390
|
// Build tree structure
|
|
488
|
-
const buildTree = () => {
|
|
391
|
+
const buildTree = async () => {
|
|
489
392
|
tree.items = [];
|
|
490
393
|
|
|
491
394
|
// Root: Requirements
|
|
@@ -495,23 +398,22 @@ async function showRequirementsTree() {
|
|
|
495
398
|
tree.items.push({ level: 1, type: 'add', label: 'ā Add new requirement', key: 'add-one' });
|
|
496
399
|
tree.items.push({ level: 1, type: 'add', label: 'ā Add multiple requirements', key: 'add-many' });
|
|
497
400
|
|
|
498
|
-
//
|
|
401
|
+
// Use pre-calculated stats and labels from shared logic
|
|
402
|
+
const stats = await countRequirements();
|
|
403
|
+
const { todoCount, toVerifyCount, verifiedCount, total, todoLabel, toVerifyLabel, verifiedLabel } = stats || {
|
|
404
|
+
todoCount: 0, toVerifyCount: 0, verifiedCount: 0, total: 0,
|
|
405
|
+
todoLabel: 'ā³ TODO (0 - 0%)', toVerifyLabel: 'ā
TO VERIFY (0 - 0%)', verifiedLabel: 'š VERIFIED (0 - 0%)'
|
|
406
|
+
};
|
|
407
|
+
|
|
499
408
|
const verifiedReqs = tree.verifiedReqs || [];
|
|
500
409
|
const verifyReqs = tree.verifyReqs || [];
|
|
501
410
|
const clarificationReqs = tree.clarificationReqs || [];
|
|
502
411
|
const todoReqs = tree.todoReqs || [];
|
|
503
412
|
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
413
|
|
|
512
414
|
// VERIFIED section (first) - only show if has requirements
|
|
513
|
-
if (verifiedReqs.length > 0) {
|
|
514
|
-
tree.items.push({ level: 1, type: 'section', label: `š
|
|
415
|
+
if (verifiedReqs.length > 0 || verifiedCount > 0) {
|
|
416
|
+
tree.items.push({ level: 1, type: 'section', label: `š ${verifiedLabel}`, key: 'verified' });
|
|
515
417
|
|
|
516
418
|
if (tree.expanded.verified) {
|
|
517
419
|
verifiedReqs.forEach((req, idx) => {
|
|
@@ -521,8 +423,8 @@ async function showRequirementsTree() {
|
|
|
521
423
|
}
|
|
522
424
|
|
|
523
425
|
// TO VERIFY section (second) - only show if has requirements
|
|
524
|
-
if (verifyReqs.length > 0) {
|
|
525
|
-
tree.items.push({ level: 1, type: 'section', label: `ā
|
|
426
|
+
if (verifyReqs.length > 0 || toVerifyCount > 0) {
|
|
427
|
+
tree.items.push({ level: 1, type: 'section', label: `ā
${toVerifyLabel}`, key: 'verify', section: 'ā
Verified by AI screenshot' });
|
|
526
428
|
|
|
527
429
|
if (tree.expanded.verify) {
|
|
528
430
|
verifyReqs.forEach((req, idx) => {
|
|
@@ -543,8 +445,8 @@ async function showRequirementsTree() {
|
|
|
543
445
|
}
|
|
544
446
|
|
|
545
447
|
// TODO section (fourth) - only show if has requirements
|
|
546
|
-
if (todoReqs.length > 0) {
|
|
547
|
-
tree.items.push({ level: 1, type: 'section', label: `ā³
|
|
448
|
+
if (todoReqs.length > 0 || todoCount > 0) {
|
|
449
|
+
tree.items.push({ level: 1, type: 'section', label: `ā³ ${todoLabel}`, key: 'todo', section: 'ā³ Requirements not yet completed' });
|
|
548
450
|
|
|
549
451
|
if (tree.expanded.todo) {
|
|
550
452
|
todoReqs.forEach((req, idx) => {
|
|
@@ -600,17 +502,6 @@ async function showRequirementsTree() {
|
|
|
600
502
|
// Reset inSection if we hit a section header that's not our target section
|
|
601
503
|
if (sectionKey === 'verify' && inSection) {
|
|
602
504
|
// 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
505
|
if (!isStillToVerify) {
|
|
615
506
|
// This will be handled by the "leaving section" check below, but ensure we don't process it as entering
|
|
616
507
|
}
|
|
@@ -658,11 +549,6 @@ async function showRequirementsTree() {
|
|
|
658
549
|
if (sectionKey === 'verify') {
|
|
659
550
|
// For TO VERIFY, only break if this is clearly a different section
|
|
660
551
|
// 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
552
|
|
|
667
553
|
if (isVerifiedSection || isTodoSection || isRecycledSection || isClarificationSection) {
|
|
668
554
|
break; // Different section, we've left TO VERIFY
|
|
@@ -688,7 +574,7 @@ async function showRequirementsTree() {
|
|
|
688
574
|
}
|
|
689
575
|
|
|
690
576
|
const details = [];
|
|
691
|
-
let
|
|
577
|
+
let pkg = null;
|
|
692
578
|
|
|
693
579
|
// Read package and description
|
|
694
580
|
for (let j = i + 1; j < lines.length; j++) {
|
|
@@ -699,14 +585,14 @@ async function showRequirementsTree() {
|
|
|
699
585
|
}
|
|
700
586
|
// Check for PACKAGE line
|
|
701
587
|
if (nextLine.startsWith('PACKAGE:')) {
|
|
702
|
-
|
|
588
|
+
pkg = nextLine.replace(/^PACKAGE:\s*/, '').trim();
|
|
703
589
|
} else if (nextLine && !nextLine.startsWith('PACKAGE:')) {
|
|
704
590
|
// Description line
|
|
705
591
|
details.push(nextLine);
|
|
706
592
|
}
|
|
707
593
|
}
|
|
708
594
|
|
|
709
|
-
requirements.push({ title, details,
|
|
595
|
+
requirements.push({ title, details, pkg, lineIndex: i });
|
|
710
596
|
}
|
|
711
597
|
}
|
|
712
598
|
|
|
@@ -837,7 +723,7 @@ async function showRequirementsTree() {
|
|
|
837
723
|
tree.recycledReqs = await loadSection('recycled', 'ā»ļø Recycled');
|
|
838
724
|
|
|
839
725
|
let inTree = true;
|
|
840
|
-
buildTree();
|
|
726
|
+
await buildTree();
|
|
841
727
|
|
|
842
728
|
while (inTree) {
|
|
843
729
|
console.clear();
|
|
@@ -944,7 +830,7 @@ async function showRequirementsTree() {
|
|
|
944
830
|
if (tree.expanded[current.key]) {
|
|
945
831
|
// Collapse expanded section
|
|
946
832
|
tree.expanded[current.key] = false;
|
|
947
|
-
buildTree();
|
|
833
|
+
await buildTree();
|
|
948
834
|
} else if (current.level > 0) {
|
|
949
835
|
// Go to parent
|
|
950
836
|
for (let i = tree.selected - 1; i >= 0; i--) {
|
|
@@ -957,10 +843,12 @@ async function showRequirementsTree() {
|
|
|
957
843
|
// At root level, go back to main menu
|
|
958
844
|
inTree = false;
|
|
959
845
|
}
|
|
960
|
-
} else if (key.name === 'up') {
|
|
846
|
+
} else if (key.name === 'k' || key.name === 'up') {
|
|
961
847
|
tree.selected = Math.max(0, tree.selected - 1);
|
|
962
|
-
|
|
848
|
+
await buildTree();
|
|
849
|
+
} else if (key.name === 'j' || key.name === 'down') {
|
|
963
850
|
tree.selected = Math.min(tree.items.length - 1, tree.selected + 1);
|
|
851
|
+
await buildTree();
|
|
964
852
|
} else if (key.name === 'right' || key.name === 'return' || key.name === 'space') {
|
|
965
853
|
const current = tree.items[tree.selected];
|
|
966
854
|
if (!current) continue; // Safety check
|
|
@@ -977,19 +865,19 @@ async function showRequirementsTree() {
|
|
|
977
865
|
} else if (current.key === 'recycled') {
|
|
978
866
|
tree.recycledReqs = await loadSection(current.key, current.section);
|
|
979
867
|
}
|
|
980
|
-
buildTree();
|
|
868
|
+
await buildTree();
|
|
981
869
|
} else {
|
|
982
870
|
tree.expanded[current.key] = false;
|
|
983
|
-
buildTree();
|
|
871
|
+
await buildTree();
|
|
984
872
|
}
|
|
985
873
|
} else if (current.type === 'requirement') {
|
|
986
874
|
// Show requirement actions
|
|
987
875
|
await showRequirementActions(current.req, current.sectionKey, tree);
|
|
988
|
-
buildTree();
|
|
876
|
+
await buildTree();
|
|
989
877
|
} else if (current.type === 'clarification') {
|
|
990
878
|
// Show clarification requirement with questions
|
|
991
879
|
await showClarificationActions(current.req, tree, loadClarification);
|
|
992
|
-
buildTree();
|
|
880
|
+
await buildTree();
|
|
993
881
|
} else if (current.type === 'verified') {
|
|
994
882
|
// Show verified item details (read-only)
|
|
995
883
|
console.clear();
|
|
@@ -1016,7 +904,7 @@ async function showRequirementsTree() {
|
|
|
1016
904
|
await handleAddRequirement(current.key);
|
|
1017
905
|
// Reload TODO section
|
|
1018
906
|
tree.todoReqs = await loadSection('todo', 'ā³ Requirements not yet completed');
|
|
1019
|
-
buildTree();
|
|
907
|
+
await buildTree();
|
|
1020
908
|
}
|
|
1021
909
|
} else if (key.name === 'r') {
|
|
1022
910
|
const current = tree.items[tree.selected];
|
|
@@ -1030,15 +918,15 @@ async function showRequirementsTree() {
|
|
|
1030
918
|
} else if (current.sectionKey === 'verify') {
|
|
1031
919
|
tree.verifyReqs = await loadSection('verify', 'ā
Verified by AI screenshot');
|
|
1032
920
|
}
|
|
1033
|
-
buildTree();
|
|
921
|
+
await buildTree();
|
|
1034
922
|
} else if (current.type === 'clarification') {
|
|
1035
923
|
await deleteClarification(current.req, tree);
|
|
1036
924
|
tree.clarificationReqs = await loadClarification();
|
|
1037
|
-
buildTree();
|
|
925
|
+
await buildTree();
|
|
1038
926
|
} else if (current.type === 'recycled') {
|
|
1039
927
|
await permanentlyDeleteRequirement(current.req, current.sectionKey, tree);
|
|
1040
928
|
tree.recycledReqs = await loadSection('recycled', 'ā»ļø Recycled');
|
|
1041
|
-
buildTree();
|
|
929
|
+
await buildTree();
|
|
1042
930
|
}
|
|
1043
931
|
} else if (key.name === 'j') {
|
|
1044
932
|
const current = tree.items[tree.selected];
|
|
@@ -1046,7 +934,7 @@ async function showRequirementsTree() {
|
|
|
1046
934
|
|
|
1047
935
|
if (current.type === 'requirement') {
|
|
1048
936
|
await moveRequirementDown(current.req, current.sectionKey, tree);
|
|
1049
|
-
buildTree();
|
|
937
|
+
await buildTree();
|
|
1050
938
|
// Move selection down to follow the item
|
|
1051
939
|
if (tree.selected < tree.items.length - 1) {
|
|
1052
940
|
tree.selected++;
|
|
@@ -1058,7 +946,7 @@ async function showRequirementsTree() {
|
|
|
1058
946
|
|
|
1059
947
|
if (current.type === 'requirement') {
|
|
1060
948
|
await moveRequirementUp(current.req, current.sectionKey, tree);
|
|
1061
|
-
buildTree();
|
|
949
|
+
await buildTree();
|
|
1062
950
|
// Move selection up to follow the item
|
|
1063
951
|
if (tree.selected > 0) {
|
|
1064
952
|
tree.selected--;
|
|
@@ -1070,7 +958,7 @@ async function showRequirementsTree() {
|
|
|
1070
958
|
|
|
1071
959
|
if (current.type === 'requirement') {
|
|
1072
960
|
await promoteRequirement(current.req, current.sectionKey, tree, loadSection, loadVerified);
|
|
1073
|
-
buildTree();
|
|
961
|
+
await buildTree();
|
|
1074
962
|
}
|
|
1075
963
|
} else if (key.name === 'd') {
|
|
1076
964
|
const current = tree.items[tree.selected];
|
|
@@ -1081,12 +969,12 @@ async function showRequirementsTree() {
|
|
|
1081
969
|
await moveClarificationToTodo(current.req, tree);
|
|
1082
970
|
tree.clarificationReqs = await loadClarification();
|
|
1083
971
|
tree.todoReqs = await loadSection('todo', 'ā³ Requirements not yet completed');
|
|
1084
|
-
buildTree();
|
|
972
|
+
await buildTree();
|
|
1085
973
|
} else if (current.type === 'requirement' || current.type === 'verified') {
|
|
1086
974
|
const sectionKey = current.type === 'verified' ? 'verified' : current.sectionKey;
|
|
1087
975
|
const reqTitle = current.type === 'verified' ? current.label : current.req.title;
|
|
1088
976
|
await demoteRequirement(reqTitle, sectionKey, tree, loadSection, loadVerified);
|
|
1089
|
-
buildTree();
|
|
977
|
+
await buildTree();
|
|
1090
978
|
}
|
|
1091
979
|
}
|
|
1092
980
|
}
|
|
@@ -1853,7 +1741,7 @@ async function permanentlyDeleteRequirement(req, sectionKey, tree) {
|
|
|
1853
1741
|
const line = lines[i].trim();
|
|
1854
1742
|
if (line.startsWith('###')) {
|
|
1855
1743
|
const title = line.replace(/^###\s*/, '').trim();
|
|
1856
|
-
if (title && title
|
|
1744
|
+
if (title && title === req.title) {
|
|
1857
1745
|
requirementStartIndex = i;
|
|
1858
1746
|
// Find the end of this requirement (next ### or ## header)
|
|
1859
1747
|
for (let j = i + 1; j < lines.length; j++) {
|
|
@@ -1898,7 +1786,7 @@ async function moveRequirementToRecycled(reqPath, requirementTitle, fromSection)
|
|
|
1898
1786
|
const line = lines[i].trim();
|
|
1899
1787
|
if (line.startsWith('###')) {
|
|
1900
1788
|
const title = line.replace(/^###\s*/, '').trim();
|
|
1901
|
-
if (title && title
|
|
1789
|
+
if (title && title === requirementTitle) {
|
|
1902
1790
|
requirementStartIndex = i;
|
|
1903
1791
|
// Find the end of this requirement (next ### or ## header)
|
|
1904
1792
|
for (let j = i + 1; j < lines.length; j++) {
|
|
@@ -2219,7 +2107,7 @@ async function handleAddRequirement(type) {
|
|
|
2219
2107
|
while (!done) {
|
|
2220
2108
|
try {
|
|
2221
2109
|
// Ask for package
|
|
2222
|
-
const { package } = await inquirer.prompt([{
|
|
2110
|
+
const { package: pkg } = await inquirer.prompt([{
|
|
2223
2111
|
type: 'list',
|
|
2224
2112
|
name: 'package',
|
|
2225
2113
|
message: `Package for requirement ${requirements.length + 1}:`,
|
|
@@ -2268,7 +2156,7 @@ async function handleAddRequirement(type) {
|
|
|
2268
2156
|
}
|
|
2269
2157
|
|
|
2270
2158
|
const description = descriptionLines.join('\n');
|
|
2271
|
-
requirements.push({ name, package, description });
|
|
2159
|
+
requirements.push({ name, package: pkg, description });
|
|
2272
2160
|
}
|
|
2273
2161
|
} catch (err) {
|
|
2274
2162
|
done = true;
|
|
@@ -2565,7 +2453,6 @@ async function showQuickMenu(items, initialSelectedIndex = 0) {
|
|
|
2565
2453
|
if (selectedIndex >= items.length) selectedIndex = 0;
|
|
2566
2454
|
|
|
2567
2455
|
let isFirstRender = true;
|
|
2568
|
-
let lastLinesPrinted = 0;
|
|
2569
2456
|
|
|
2570
2457
|
// Helper to calculate visual lines occupied by text
|
|
2571
2458
|
const getVisualLineCount = (text) => {
|
|
@@ -2751,22 +2638,27 @@ async function showQuickMenu(items, initialSelectedIndex = 0) {
|
|
|
2751
2638
|
|
|
2752
2639
|
// Arrow keys for navigation
|
|
2753
2640
|
if (key.name === 'up') {
|
|
2754
|
-
//
|
|
2755
|
-
let
|
|
2756
|
-
while (
|
|
2757
|
-
|
|
2641
|
+
// Search backwards for the previous selectable item
|
|
2642
|
+
let testIndex = selectedIndex - 1;
|
|
2643
|
+
while (testIndex >= 0) {
|
|
2644
|
+
if (items[testIndex].type !== 'blank' && items[testIndex].type !== 'info') {
|
|
2645
|
+
selectedIndex = testIndex;
|
|
2646
|
+
await displayMenu();
|
|
2647
|
+
break;
|
|
2648
|
+
}
|
|
2649
|
+
testIndex--;
|
|
2758
2650
|
}
|
|
2759
|
-
selectedIndex = newIndex;
|
|
2760
|
-
await displayMenu();
|
|
2761
2651
|
} else if (key.name === 'down') {
|
|
2762
|
-
//
|
|
2763
|
-
let
|
|
2764
|
-
while (
|
|
2765
|
-
|
|
2652
|
+
// Search forwards for the next selectable item
|
|
2653
|
+
let testIndex = selectedIndex + 1;
|
|
2654
|
+
while (testIndex < items.length) {
|
|
2655
|
+
if (items[testIndex].type !== 'blank' && items[testIndex].type !== 'info') {
|
|
2656
|
+
selectedIndex = testIndex;
|
|
2657
|
+
await displayMenu();
|
|
2658
|
+
break;
|
|
2659
|
+
}
|
|
2660
|
+
testIndex++;
|
|
2766
2661
|
}
|
|
2767
|
-
selectedIndex = newIndex;
|
|
2768
|
-
await displayMenu();
|
|
2769
|
-
|
|
2770
2662
|
} else if (key.name === 'return' || key.name === 'right') {
|
|
2771
2663
|
// Don't allow selecting blank or info lines
|
|
2772
2664
|
if (items[selectedIndex].type !== 'blank' && items[selectedIndex].type !== 'info') {
|
|
@@ -2789,24 +2681,59 @@ async function showProviderManagerMenu() {
|
|
|
2789
2681
|
let selectedIndex = 0;
|
|
2790
2682
|
let dirty = false;
|
|
2791
2683
|
|
|
2792
|
-
|
|
2793
|
-
const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
|
|
2794
|
-
const providerManager = new ProviderManager();
|
|
2684
|
+
const { fetchQuotaForAgent } = require('vibecodingmachine-core/src/quota-management');
|
|
2795
2685
|
|
|
2796
|
-
const
|
|
2686
|
+
const debugQuota = process.env.VCM_DEBUG_QUOTA === '1' || process.env.VCM_DEBUG_QUOTA === 'true';
|
|
2687
|
+
|
|
2688
|
+
const formatDuration = (ms) => {
|
|
2689
|
+
if (!ms || ms <= 0) return 'now';
|
|
2690
|
+
const totalSeconds = Math.ceil(ms / 1000);
|
|
2691
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
2692
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
2693
|
+
const seconds = totalSeconds % 60;
|
|
2694
|
+
|
|
2695
|
+
if (hours > 0) return `${hours}h ${minutes}m`;
|
|
2696
|
+
if (minutes > 0) return `${minutes}m ${seconds}s`;
|
|
2697
|
+
return `${seconds}s`;
|
|
2698
|
+
};
|
|
2699
|
+
|
|
2700
|
+
const render = async () => {
|
|
2797
2701
|
process.stdout.write('\x1Bc');
|
|
2798
2702
|
console.log(chalk.bold.cyan('ā Provider Order & Availability\n'));
|
|
2703
|
+
|
|
2704
|
+
// Fetch quota info
|
|
2705
|
+
const sharedAuth = require('vibecodingmachine-core/src/auth/shared-auth-storage');
|
|
2706
|
+
const autoConfig = await getAutoConfig();
|
|
2707
|
+
const quotaInfo = await sharedAuth.canRunAutoMode();
|
|
2708
|
+
const remaining = Math.max(0, (quotaInfo.maxIterations || 10) - (quotaInfo.todayUsage || 0));
|
|
2709
|
+
|
|
2710
|
+
// Calculate time until reset (midnight)
|
|
2711
|
+
const now = new Date();
|
|
2712
|
+
const tonight = new Date(now);
|
|
2713
|
+
tonight.setHours(24, 0, 0, 0);
|
|
2714
|
+
const msUntilReset = tonight.getTime() - now.getTime();
|
|
2715
|
+
const hoursUntilReset = Math.floor(msUntilReset / (1000 * 60 * 60));
|
|
2716
|
+
const minsUntilReset = Math.floor((msUntilReset % (1000 * 60 * 60)) / (1000 * 60));
|
|
2717
|
+
|
|
2718
|
+
// Display quota as time-based instead of numeric (0/1 or 1/1 format)
|
|
2719
|
+
let quotaDisplay;
|
|
2720
|
+
if (remaining === 0) {
|
|
2721
|
+
// Rate limit active - show when it resets (in red)
|
|
2722
|
+
quotaDisplay = chalk.gray(' Overall Quota: ') + chalk.red(`ā° Rate limit resets in ${hoursUntilReset}h ${minsUntilReset}m`);
|
|
2723
|
+
} else {
|
|
2724
|
+
// Quota available - show when it resets (in green)
|
|
2725
|
+
quotaDisplay = chalk.gray(' Overall Quota: ') + chalk.green(`ā Available (${remaining}/${quotaInfo.maxIterations})`) + chalk.gray(' ⢠Resets in ') + chalk.cyan(`${hoursUntilReset}h ${minsUntilReset}m`);
|
|
2726
|
+
}
|
|
2727
|
+
console.log(quotaDisplay);
|
|
2799
2728
|
console.log(chalk.gray(' ā/ā move selection j/k reorder e enable d disable Space toggle Enter save/select Esc cancel\n'));
|
|
2800
2729
|
|
|
2801
|
-
order.
|
|
2730
|
+
for (let idx = 0; idx < order.length; idx++) {
|
|
2731
|
+
const id = order[idx];
|
|
2802
2732
|
const def = defMap.get(id);
|
|
2803
|
-
if (!def)
|
|
2733
|
+
if (!def) continue;
|
|
2804
2734
|
const isSelected = idx === selectedIndex;
|
|
2805
2735
|
const isEnabled = enabled[id] !== false;
|
|
2806
2736
|
|
|
2807
|
-
// Check for rate limits
|
|
2808
|
-
const timeUntilReset = providerManager.getTimeUntilReset(id, def.model || id);
|
|
2809
|
-
|
|
2810
2737
|
// Check for Kiro installation
|
|
2811
2738
|
let isInstalled = true;
|
|
2812
2739
|
if (id === 'kiro') {
|
|
@@ -2830,50 +2757,53 @@ async function showProviderManagerMenu() {
|
|
|
2830
2757
|
const prefix = isSelected ? chalk.cyan('āÆ') : ' ';
|
|
2831
2758
|
let line = `${prefix} ${statusEmoji} ${idx + 1}. ${def.name} ${chalk.gray(`(${def.id})`)} ${typeLabel}`;
|
|
2832
2759
|
|
|
2833
|
-
//
|
|
2834
|
-
|
|
2835
|
-
//
|
|
2836
|
-
|
|
2837
|
-
|
|
2760
|
+
// Fetch and display specific quota for this agent
|
|
2761
|
+
try {
|
|
2762
|
+
// Find the active model for this provider if possible
|
|
2763
|
+
let model = def.defaultModel || id;
|
|
2764
|
+
if (id === 'groq') model = autoConfig.groqModel || model;
|
|
2765
|
+
else if (id === 'anthropic') model = autoConfig.anthropicModel || model;
|
|
2766
|
+
else if (id === 'ollama') {
|
|
2767
|
+
const preferredModel = autoConfig.llmModel && autoConfig.llmModel.includes('ollama/')
|
|
2768
|
+
? autoConfig.llmModel.split('/')[1]
|
|
2769
|
+
: autoConfig.llmModel || autoConfig.aiderModel;
|
|
2770
|
+
model = (preferredModel && preferredModel !== id) ? preferredModel : model;
|
|
2771
|
+
}
|
|
2838
2772
|
|
|
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)}`;
|
|
2773
|
+
const agentId = `${id}:${model}`;
|
|
2774
|
+
const quota = await fetchQuotaForAgent(agentId);
|
|
2775
|
+
|
|
2776
|
+
if (debugQuota) {
|
|
2777
|
+
const resetMs = quota?.resetsAt ? (new Date(quota.resetsAt).getTime() - Date.now()) : null;
|
|
2778
|
+
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
2779
|
}
|
|
2871
2780
|
|
|
2872
|
-
|
|
2781
|
+
if (quota.type === 'infinite') {
|
|
2782
|
+
line += ` ${chalk.gray('[Quota: Infinite]')}`;
|
|
2783
|
+
} else if (quota.type === 'rate-limit') {
|
|
2784
|
+
if (quota.isExceeded()) {
|
|
2785
|
+
if (quota.resetsAt) {
|
|
2786
|
+
const msUntilReset = new Date(quota.resetsAt).getTime() - Date.now();
|
|
2787
|
+
line += ` ${chalk.red(`[ā³ resets in ${formatDuration(msUntilReset)}]`)}`;
|
|
2788
|
+
} else {
|
|
2789
|
+
line += ` ${chalk.red('[Rate limited]')}`;
|
|
2790
|
+
}
|
|
2791
|
+
} else {
|
|
2792
|
+
// Show time until rate limit starts (when it resets)
|
|
2793
|
+
if (quota.resetsAt) {
|
|
2794
|
+
const msUntilReset = new Date(quota.resetsAt).getTime() - Date.now();
|
|
2795
|
+
line += ` ${chalk.green(`[ā available ⢠resets in ${formatDuration(msUntilReset)}]`)}`;
|
|
2796
|
+
} else {
|
|
2797
|
+
line += ` ${chalk.green('[Available]')}`;
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
} catch (e) {
|
|
2802
|
+
// Silently skip if quota fetch fails
|
|
2873
2803
|
}
|
|
2874
2804
|
|
|
2875
2805
|
console.log(line);
|
|
2876
|
-
}
|
|
2806
|
+
}
|
|
2877
2807
|
|
|
2878
2808
|
console.log();
|
|
2879
2809
|
if (dirty) {
|
|
@@ -2883,7 +2813,12 @@ async function showProviderManagerMenu() {
|
|
|
2883
2813
|
}
|
|
2884
2814
|
};
|
|
2885
2815
|
|
|
2886
|
-
|
|
2816
|
+
if (process.env.VCM_RENDER_ONCE === '1' || process.env.VCM_RENDER_ONCE === 'true') {
|
|
2817
|
+
await render();
|
|
2818
|
+
return;
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
return new Promise(async (resolve) => {
|
|
2887
2822
|
const cleanup = () => {
|
|
2888
2823
|
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
2889
2824
|
process.stdin.setRawMode(false);
|
|
@@ -2926,15 +2861,15 @@ async function showProviderManagerMenu() {
|
|
|
2926
2861
|
resolve(null);
|
|
2927
2862
|
};
|
|
2928
2863
|
|
|
2929
|
-
const moveSelection = (delta) => {
|
|
2864
|
+
const moveSelection = async (delta) => {
|
|
2930
2865
|
const next = selectedIndex + delta;
|
|
2931
2866
|
if (next >= 0 && next < order.length) {
|
|
2932
2867
|
selectedIndex = next;
|
|
2933
|
-
render();
|
|
2868
|
+
await render();
|
|
2934
2869
|
}
|
|
2935
2870
|
};
|
|
2936
2871
|
|
|
2937
|
-
const reorder = (delta) => {
|
|
2872
|
+
const reorder = async (delta) => {
|
|
2938
2873
|
const target = selectedIndex + delta;
|
|
2939
2874
|
if (target < 0 || target >= order.length) return;
|
|
2940
2875
|
const temp = order[selectedIndex];
|
|
@@ -2942,17 +2877,17 @@ async function showProviderManagerMenu() {
|
|
|
2942
2877
|
order[target] = temp;
|
|
2943
2878
|
selectedIndex = target;
|
|
2944
2879
|
dirty = true;
|
|
2945
|
-
render();
|
|
2880
|
+
await render();
|
|
2946
2881
|
};
|
|
2947
2882
|
|
|
2948
|
-
const toggle = (value) => {
|
|
2883
|
+
const toggle = async (value) => {
|
|
2949
2884
|
const id = order[selectedIndex];
|
|
2950
2885
|
enabled[id] = value;
|
|
2951
2886
|
dirty = true;
|
|
2952
|
-
render();
|
|
2887
|
+
await render();
|
|
2953
2888
|
};
|
|
2954
2889
|
|
|
2955
|
-
const onKeypress = (str, key = {}) => {
|
|
2890
|
+
const onKeypress = async (str, key = {}) => {
|
|
2956
2891
|
if (key.ctrl && key.name === 'c') {
|
|
2957
2892
|
cancel();
|
|
2958
2893
|
return;
|
|
@@ -2960,25 +2895,25 @@ async function showProviderManagerMenu() {
|
|
|
2960
2895
|
|
|
2961
2896
|
switch (key.name) {
|
|
2962
2897
|
case 'up':
|
|
2963
|
-
moveSelection(-1);
|
|
2898
|
+
await moveSelection(-1);
|
|
2964
2899
|
break;
|
|
2965
2900
|
case 'down':
|
|
2966
|
-
moveSelection(1);
|
|
2901
|
+
await moveSelection(1);
|
|
2967
2902
|
break;
|
|
2968
2903
|
case 'j':
|
|
2969
|
-
reorder(1);
|
|
2904
|
+
await reorder(1);
|
|
2970
2905
|
break;
|
|
2971
2906
|
case 'k':
|
|
2972
|
-
reorder(-1);
|
|
2907
|
+
await reorder(-1);
|
|
2973
2908
|
break;
|
|
2974
2909
|
case 'e':
|
|
2975
|
-
toggle(true);
|
|
2910
|
+
await toggle(true);
|
|
2976
2911
|
break;
|
|
2977
2912
|
case 'd':
|
|
2978
|
-
toggle(false);
|
|
2913
|
+
await toggle(false);
|
|
2979
2914
|
break;
|
|
2980
2915
|
case 'space':
|
|
2981
|
-
toggle(!(enabled[order[selectedIndex]] !== false));
|
|
2916
|
+
await toggle(!(enabled[order[selectedIndex]] !== false));
|
|
2982
2917
|
break;
|
|
2983
2918
|
case 'return':
|
|
2984
2919
|
saveAndExit(order[selectedIndex]);
|
|
@@ -3000,11 +2935,11 @@ async function showProviderManagerMenu() {
|
|
|
3000
2935
|
process.stdin.on('keypress', onKeypress);
|
|
3001
2936
|
process.stdin.resume();
|
|
3002
2937
|
|
|
3003
|
-
render();
|
|
2938
|
+
await render();
|
|
3004
2939
|
});
|
|
3005
2940
|
}
|
|
3006
2941
|
|
|
3007
|
-
async function showSettings() {
|
|
2942
|
+
/* async function showSettings() {
|
|
3008
2943
|
console.log(chalk.bold.cyan('\nāļø Settings\n'));
|
|
3009
2944
|
|
|
3010
2945
|
const { setConfigValue } = require('vibecodingmachine-core');
|
|
@@ -3096,7 +3031,7 @@ async function showSettings() {
|
|
|
3096
3031
|
async function showCloudSyncMenu() {
|
|
3097
3032
|
console.clear();
|
|
3098
3033
|
console.log(chalk.bold.cyan('\nāļø Cloud Sync Management\n'));
|
|
3099
|
-
|
|
3034
|
+
|
|
3100
3035
|
// Check if cloud sync is configured
|
|
3101
3036
|
try {
|
|
3102
3037
|
const SyncEngine = require('vibecodingmachine-core/src/sync/sync-engine');
|
|
@@ -3110,7 +3045,7 @@ async function showCloudSyncMenu() {
|
|
|
3110
3045
|
console.log(chalk.gray('2. Add AWS configuration to your .env file'));
|
|
3111
3046
|
console.log(chalk.gray('3. Restart vcm\n'));
|
|
3112
3047
|
console.log(chalk.gray('For more info, see: ') + chalk.cyan('docs/CLOUD_SYNC.md\n'));
|
|
3113
|
-
|
|
3048
|
+
|
|
3114
3049
|
console.log(chalk.gray('Press Enter to return to main menu...'));
|
|
3115
3050
|
await new Promise(resolve => {
|
|
3116
3051
|
const rl = readline.createInterface({
|
|
@@ -3124,10 +3059,10 @@ async function showCloudSyncMenu() {
|
|
|
3124
3059
|
});
|
|
3125
3060
|
return;
|
|
3126
3061
|
}
|
|
3127
|
-
|
|
3062
|
+
|
|
3128
3063
|
const computerCommands = require('../commands/computers');
|
|
3129
3064
|
const syncCommands = require('../commands/sync');
|
|
3130
|
-
|
|
3065
|
+
|
|
3131
3066
|
const choices = [
|
|
3132
3067
|
{ name: 'š View All Computers', value: 'computers' },
|
|
3133
3068
|
{ name: 'š„ļø Manage Another Computer\'s Requirements', value: 'manage-remote' },
|
|
@@ -3139,7 +3074,7 @@ async function showCloudSyncMenu() {
|
|
|
3139
3074
|
{ name: 'šÆ Update Focus Area', value: 'update-focus' },
|
|
3140
3075
|
{ name: chalk.gray('ā Back to Main Menu'), value: 'back' }
|
|
3141
3076
|
];
|
|
3142
|
-
|
|
3077
|
+
|
|
3143
3078
|
const { action } = await inquirer.prompt([
|
|
3144
3079
|
{
|
|
3145
3080
|
type: 'list',
|
|
@@ -3148,7 +3083,7 @@ async function showCloudSyncMenu() {
|
|
|
3148
3083
|
choices: choices
|
|
3149
3084
|
}
|
|
3150
3085
|
]);
|
|
3151
|
-
|
|
3086
|
+
|
|
3152
3087
|
switch (action) {
|
|
3153
3088
|
case 'computers':
|
|
3154
3089
|
try {
|
|
@@ -3170,23 +3105,23 @@ async function showCloudSyncMenu() {
|
|
|
3170
3105
|
});
|
|
3171
3106
|
await showCloudSyncMenu();
|
|
3172
3107
|
break;
|
|
3173
|
-
|
|
3108
|
+
|
|
3174
3109
|
case 'manage-remote':
|
|
3175
3110
|
try {
|
|
3176
3111
|
// First, get list of computers
|
|
3177
3112
|
const SyncEngine = require('vibecodingmachine-core/src/sync/sync-engine');
|
|
3178
3113
|
const { ScanCommand } = require('@aws-sdk/lib-dynamodb');
|
|
3179
|
-
|
|
3114
|
+
|
|
3180
3115
|
const syncEngine = new SyncEngine();
|
|
3181
3116
|
await syncEngine.initialize();
|
|
3182
|
-
|
|
3117
|
+
|
|
3183
3118
|
const tableName = 'vibecodingmachine-computers';
|
|
3184
3119
|
const command = new ScanCommand({ TableName: tableName });
|
|
3185
3120
|
const response = await syncEngine.dynamoClient.send(command);
|
|
3186
3121
|
const computers = response.Items || [];
|
|
3187
|
-
|
|
3122
|
+
|
|
3188
3123
|
syncEngine.stop();
|
|
3189
|
-
|
|
3124
|
+
|
|
3190
3125
|
if (computers.length === 0) {
|
|
3191
3126
|
console.log(chalk.yellow('\nā No computers registered yet.\n'));
|
|
3192
3127
|
} else {
|
|
@@ -3196,7 +3131,7 @@ async function showCloudSyncMenu() {
|
|
|
3196
3131
|
value: c.computerId
|
|
3197
3132
|
}));
|
|
3198
3133
|
computerChoices.push({ name: chalk.gray('ā Cancel'), value: null });
|
|
3199
|
-
|
|
3134
|
+
|
|
3200
3135
|
const { selectedComputer } = await inquirer.prompt([
|
|
3201
3136
|
{
|
|
3202
3137
|
type: 'list',
|
|
@@ -3205,7 +3140,7 @@ async function showCloudSyncMenu() {
|
|
|
3205
3140
|
choices: computerChoices
|
|
3206
3141
|
}
|
|
3207
3142
|
]);
|
|
3208
|
-
|
|
3143
|
+
|
|
3209
3144
|
if (selectedComputer) {
|
|
3210
3145
|
const remoteReqCommands = require('../commands/requirements-remote');
|
|
3211
3146
|
await remoteReqCommands.manageRemoteRequirements(selectedComputer);
|
|
@@ -3216,7 +3151,7 @@ async function showCloudSyncMenu() {
|
|
|
3216
3151
|
}
|
|
3217
3152
|
await showCloudSyncMenu();
|
|
3218
3153
|
break;
|
|
3219
|
-
|
|
3154
|
+
|
|
3220
3155
|
case 'sync-now':
|
|
3221
3156
|
try {
|
|
3222
3157
|
await syncCommands.syncNow();
|
|
@@ -3236,7 +3171,7 @@ async function showCloudSyncMenu() {
|
|
|
3236
3171
|
});
|
|
3237
3172
|
await showCloudSyncMenu();
|
|
3238
3173
|
break;
|
|
3239
|
-
|
|
3174
|
+
|
|
3240
3175
|
case 'sync-status':
|
|
3241
3176
|
try {
|
|
3242
3177
|
await syncCommands.syncStatus();
|
|
@@ -3256,7 +3191,7 @@ async function showCloudSyncMenu() {
|
|
|
3256
3191
|
});
|
|
3257
3192
|
await showCloudSyncMenu();
|
|
3258
3193
|
break;
|
|
3259
|
-
|
|
3194
|
+
|
|
3260
3195
|
case 'sync-history':
|
|
3261
3196
|
try {
|
|
3262
3197
|
await syncCommands.viewHistory({ limit: 50 });
|
|
@@ -3276,7 +3211,7 @@ async function showCloudSyncMenu() {
|
|
|
3276
3211
|
});
|
|
3277
3212
|
await showCloudSyncMenu();
|
|
3278
3213
|
break;
|
|
3279
|
-
|
|
3214
|
+
|
|
3280
3215
|
case 'sync-queue':
|
|
3281
3216
|
try {
|
|
3282
3217
|
await syncCommands.viewQueue();
|
|
@@ -3296,7 +3231,7 @@ async function showCloudSyncMenu() {
|
|
|
3296
3231
|
});
|
|
3297
3232
|
await showCloudSyncMenu();
|
|
3298
3233
|
break;
|
|
3299
|
-
|
|
3234
|
+
|
|
3300
3235
|
case 'register':
|
|
3301
3236
|
try {
|
|
3302
3237
|
const { focusArea } = await inquirer.prompt([
|
|
@@ -3324,7 +3259,7 @@ async function showCloudSyncMenu() {
|
|
|
3324
3259
|
});
|
|
3325
3260
|
await showCloudSyncMenu();
|
|
3326
3261
|
break;
|
|
3327
|
-
|
|
3262
|
+
|
|
3328
3263
|
case 'update-focus':
|
|
3329
3264
|
try {
|
|
3330
3265
|
const { newFocus } = await inquirer.prompt([
|
|
@@ -3353,7 +3288,7 @@ async function showCloudSyncMenu() {
|
|
|
3353
3288
|
});
|
|
3354
3289
|
await showCloudSyncMenu();
|
|
3355
3290
|
break;
|
|
3356
|
-
|
|
3291
|
+
|
|
3357
3292
|
case 'back':
|
|
3358
3293
|
// Return to main menu
|
|
3359
3294
|
break;
|
|
@@ -3400,6 +3335,11 @@ async function startInteractive() {
|
|
|
3400
3335
|
|
|
3401
3336
|
await showWelcomeScreen();
|
|
3402
3337
|
|
|
3338
|
+
if (process.env.VCM_OPEN_PROVIDER_MENU === '1' || process.env.VCM_OPEN_PROVIDER_MENU === 'true') {
|
|
3339
|
+
await showProviderManagerMenu();
|
|
3340
|
+
return;
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3403
3343
|
let exit = false;
|
|
3404
3344
|
let lastSelectedIndex = 0; // Track last selected menu item
|
|
3405
3345
|
while (!exit) {
|
|
@@ -3572,21 +3512,31 @@ async function startInteractive() {
|
|
|
3572
3512
|
value: 'setting:hostname'
|
|
3573
3513
|
});
|
|
3574
3514
|
|
|
3515
|
+
// Add Stages configuration
|
|
3516
|
+
const { getStages } = require('./config');
|
|
3517
|
+
const configuredStages = await getStages();
|
|
3518
|
+
const stagesCount = configuredStages.length;
|
|
3519
|
+
items.push({
|
|
3520
|
+
type: 'setting',
|
|
3521
|
+
name: ` āā Configure Stages: ${chalk.cyan(stagesCount + ' stages')}`,
|
|
3522
|
+
value: 'setting:stages'
|
|
3523
|
+
});
|
|
3524
|
+
|
|
3575
3525
|
// Cloud Sync Status
|
|
3576
3526
|
try {
|
|
3577
3527
|
const SyncEngine = require('vibecodingmachine-core/src/sync/sync-engine');
|
|
3578
3528
|
const syncEngine = new SyncEngine();
|
|
3579
|
-
|
|
3529
|
+
|
|
3580
3530
|
// Try to initialize, but don't fail if AWS not configured
|
|
3581
3531
|
try {
|
|
3582
3532
|
await syncEngine.initialize();
|
|
3583
3533
|
const syncStatus = syncEngine.getStatus();
|
|
3584
3534
|
syncEngine.stop();
|
|
3585
|
-
|
|
3535
|
+
|
|
3586
3536
|
const onlineIcon = syncStatus.isOnline ? chalk.green('ā') : chalk.red('ā');
|
|
3587
3537
|
const onlineText = syncStatus.isOnline ? 'Online' : 'Offline';
|
|
3588
3538
|
const queueText = syncStatus.queuedChanges > 0 ? chalk.yellow(` (${syncStatus.queuedChanges} queued)`) : '';
|
|
3589
|
-
|
|
3539
|
+
|
|
3590
3540
|
items.push({
|
|
3591
3541
|
type: 'setting',
|
|
3592
3542
|
name: `Cloud Sync: ${onlineIcon} ${onlineText}${queueText}`,
|
|
@@ -3739,7 +3689,6 @@ async function startInteractive() {
|
|
|
3739
3689
|
const path = require('path');
|
|
3740
3690
|
const os = require('os');
|
|
3741
3691
|
const yaml = require('js-yaml');
|
|
3742
|
-
const { spawn } = require('child_process');
|
|
3743
3692
|
const configPath = path.join(os.homedir(), '.continue', 'config.yaml');
|
|
3744
3693
|
|
|
3745
3694
|
if (fs.existsSync(configPath)) {
|
|
@@ -3930,6 +3879,53 @@ async function startInteractive() {
|
|
|
3930
3879
|
await showWelcomeScreen();
|
|
3931
3880
|
break;
|
|
3932
3881
|
}
|
|
3882
|
+
case 'setting:stages': {
|
|
3883
|
+
// Configure stages
|
|
3884
|
+
const { getStages, setStages, DEFAULT_STAGES } = require('./config');
|
|
3885
|
+
const inquirer = require('inquirer');
|
|
3886
|
+
|
|
3887
|
+
const currentStages = await getStages();
|
|
3888
|
+
|
|
3889
|
+
console.log(chalk.cyan('\nšØ Configure Workflow Stages\n'));
|
|
3890
|
+
console.log(chalk.gray('Select the stages you want to include in the auto-mode workflow.'));
|
|
3891
|
+
console.log(chalk.gray('Stages will be executed in the order shown.\n'));
|
|
3892
|
+
|
|
3893
|
+
const { selectedStages } = await inquirer.prompt([
|
|
3894
|
+
{
|
|
3895
|
+
type: 'checkbox',
|
|
3896
|
+
name: 'selectedStages',
|
|
3897
|
+
message: 'Select stages:',
|
|
3898
|
+
choices: DEFAULT_STAGES.map(stage => ({
|
|
3899
|
+
name: stage,
|
|
3900
|
+
checked: currentStages.includes(stage)
|
|
3901
|
+
})),
|
|
3902
|
+
validate: (answer) => {
|
|
3903
|
+
if (answer.length < 1) {
|
|
3904
|
+
return 'You must select at least one stage.';
|
|
3905
|
+
}
|
|
3906
|
+
return true;
|
|
3907
|
+
},
|
|
3908
|
+
loop: false,
|
|
3909
|
+
pageSize: 15
|
|
3910
|
+
}
|
|
3911
|
+
]);
|
|
3912
|
+
|
|
3913
|
+
// Preserve order from DEFAULT_STAGES for selected items
|
|
3914
|
+
// This ensures stages always run in the correct logical order
|
|
3915
|
+
const newStages = DEFAULT_STAGES.filter(stage => selectedStages.includes(stage));
|
|
3916
|
+
|
|
3917
|
+
await setStages(newStages);
|
|
3918
|
+
console.log(chalk.green('\nā'), `Stages configuration updated: ${newStages.join(' ā ')}\n`);
|
|
3919
|
+
|
|
3920
|
+
const { continue: _ } = await inquirer.prompt([{
|
|
3921
|
+
type: 'input',
|
|
3922
|
+
name: 'continue',
|
|
3923
|
+
message: 'Press Enter to return to menu...'
|
|
3924
|
+
}]);
|
|
3925
|
+
|
|
3926
|
+
await showWelcomeScreen();
|
|
3927
|
+
break;
|
|
3928
|
+
}
|
|
3933
3929
|
case 'setting:auto-start': {
|
|
3934
3930
|
try {
|
|
3935
3931
|
console.log(chalk.bold.cyan('\nš Starting Auto Mode...\n'));
|
|
@@ -4116,7 +4112,7 @@ async function startInteractive() {
|
|
|
4116
4112
|
case 'repo:init':
|
|
4117
4113
|
await repo.initRepo();
|
|
4118
4114
|
break;
|
|
4119
|
-
|
|
4115
|
+
|
|
4120
4116
|
case 'computers:list': {
|
|
4121
4117
|
const computerCommands = require('../commands/computers');
|
|
4122
4118
|
await computerCommands.listComputers();
|
|
@@ -4134,7 +4130,7 @@ async function startInteractive() {
|
|
|
4134
4130
|
await showWelcomeScreen();
|
|
4135
4131
|
break;
|
|
4136
4132
|
}
|
|
4137
|
-
|
|
4133
|
+
|
|
4138
4134
|
case 'sync:now': {
|
|
4139
4135
|
const syncCommands = require('../commands/sync');
|
|
4140
4136
|
await syncCommands.syncNow();
|
|
@@ -4152,13 +4148,13 @@ async function startInteractive() {
|
|
|
4152
4148
|
await showWelcomeScreen();
|
|
4153
4149
|
break;
|
|
4154
4150
|
}
|
|
4155
|
-
|
|
4151
|
+
|
|
4156
4152
|
case 'setting:cloud-sync': {
|
|
4157
4153
|
await showCloudSyncMenu();
|
|
4158
4154
|
await showWelcomeScreen();
|
|
4159
4155
|
break;
|
|
4160
4156
|
}
|
|
4161
|
-
|
|
4157
|
+
|
|
4162
4158
|
case 'setting:cloud-sync-setup': {
|
|
4163
4159
|
console.clear();
|
|
4164
4160
|
console.log(chalk.bold.cyan('\nāļø Cloud Sync Setup\n'));
|
|
@@ -4168,7 +4164,7 @@ async function startInteractive() {
|
|
|
4168
4164
|
console.log(chalk.gray('2. Add AWS configuration to your .env file'));
|
|
4169
4165
|
console.log(chalk.gray('3. Register this computer with: ') + chalk.cyan('vcm computer:register "<focus>"'));
|
|
4170
4166
|
console.log(chalk.gray('\nFor more info, see: ') + chalk.cyan('docs/CLOUD_SYNC.md\n'));
|
|
4171
|
-
|
|
4167
|
+
|
|
4172
4168
|
console.log(chalk.gray('Press Enter to continue...'));
|
|
4173
4169
|
await new Promise(resolve => {
|
|
4174
4170
|
const rl = readline.createInterface({
|