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/bin/vibecodingmachine.js
CHANGED
|
@@ -35,7 +35,13 @@ const { cleanupBrokenAssets } = require('../src/utils/asset-cleanup');
|
|
|
35
35
|
const { program } = require('commander');
|
|
36
36
|
const chalk = require('chalk');
|
|
37
37
|
const inquirer = require('inquirer');
|
|
38
|
-
const { execSync } = require('child_process');
|
|
38
|
+
const { execSync, spawn } = require('child_process');
|
|
39
|
+
const ora = require('ora');
|
|
40
|
+
const https = require('https');
|
|
41
|
+
const os = require('os');
|
|
42
|
+
const { pipeline } = require('stream');
|
|
43
|
+
const { promisify } = require('util');
|
|
44
|
+
const streamPipeline = promisify(pipeline);
|
|
39
45
|
// const path = require('path'); // Moved to top for .env.auth loading
|
|
40
46
|
const packageJson = require('../package.json');
|
|
41
47
|
|
|
@@ -117,6 +123,11 @@ program
|
|
|
117
123
|
.option('--no-never-stop', 'Disable never stop mode')
|
|
118
124
|
.action(autoCommands.config);
|
|
119
125
|
|
|
126
|
+
program
|
|
127
|
+
.command('auto:agents')
|
|
128
|
+
.description('List available agents and their quota status')
|
|
129
|
+
.action(autoCommands.listAgents);
|
|
130
|
+
|
|
120
131
|
// Requirements management commands
|
|
121
132
|
program
|
|
122
133
|
.command('req:list')
|
|
@@ -298,18 +309,33 @@ program
|
|
|
298
309
|
// Error handling
|
|
299
310
|
process.on('uncaughtException', (error) => {
|
|
300
311
|
console.error(chalk.red('Error:'), error.message);
|
|
312
|
+
if (process.env.DEBUG) {
|
|
313
|
+
console.error(chalk.gray('Stack:'), error.stack);
|
|
314
|
+
}
|
|
301
315
|
process.exit(1);
|
|
302
316
|
});
|
|
303
317
|
|
|
304
318
|
process.on('unhandledRejection', (error) => {
|
|
305
319
|
console.error(chalk.red('Error:'), error.message);
|
|
320
|
+
if (process.env.DEBUG) {
|
|
321
|
+
console.error(chalk.gray('Stack:'), error.stack);
|
|
322
|
+
}
|
|
306
323
|
process.exit(1);
|
|
307
324
|
});
|
|
308
325
|
|
|
309
326
|
// Check for updates and display notification
|
|
310
327
|
async function checkForUpdates() {
|
|
311
328
|
try {
|
|
329
|
+
if (process.env.VCM_SKIP_UPDATE_CHECK === '1' || process.env.VCM_SKIP_UPDATE_CHECK === 'true') {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
312
332
|
const { checkForCLIUpdates } = require('vibecodingmachine-core');
|
|
333
|
+
// If running inside the repository (local development), skip update checks entirely
|
|
334
|
+
const isDevWorkspace = fs.existsSync(path.join(rootDir, '.git'));
|
|
335
|
+
if (isDevWorkspace) {
|
|
336
|
+
console.log(chalk.yellow('\nDetected local development workspace; skipping update check.'));
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
313
339
|
console.log(chalk.gray(`\nđ Checking for updates... (current: v${packageJson.version})`));
|
|
314
340
|
const updateInfo = await checkForCLIUpdates(packageJson.version);
|
|
315
341
|
console.log(chalk.gray(` Update check result: ${JSON.stringify(updateInfo)}\n`));
|
|
@@ -335,14 +361,136 @@ async function checkForUpdates() {
|
|
|
335
361
|
if (answer.shouldUpdate) {
|
|
336
362
|
console.log(chalk.cyan('\nđ Updating VibeCodingMachine CLI...\n'));
|
|
337
363
|
try {
|
|
338
|
-
//
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
364
|
+
// If we're running from the repository (local dev), avoid performing a global install
|
|
365
|
+
const isDevWorkspace = fs.existsSync(path.join(rootDir, '.git'));
|
|
366
|
+
if (isDevWorkspace) {
|
|
367
|
+
console.log(chalk.yellow('\nDetected local development workspace; skipping automatic global install.'));
|
|
368
|
+
console.log(chalk.gray(' To update the globally installed CLI run:') + ' ' + chalk.bold.white('npm install -g vibecodingmachine-cli@latest\n'));
|
|
369
|
+
// Do not attempt global install during development to avoid confusing local dev flow
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const spinner = ora('Fetching package metadata...').start();
|
|
373
|
+
|
|
374
|
+
// Get latest package metadata from npm registry
|
|
375
|
+
const registryUrl = 'https://registry.npmjs.org/vibecodingmachine-cli/latest';
|
|
376
|
+
const meta = await new Promise((resolve, reject) => {
|
|
377
|
+
https.get(registryUrl, (res) => {
|
|
378
|
+
let data = '';
|
|
379
|
+
res.on('data', (chunk) => data += chunk);
|
|
380
|
+
res.on('end', () => {
|
|
381
|
+
try {
|
|
382
|
+
resolve(JSON.parse(data));
|
|
383
|
+
} catch (err) {
|
|
384
|
+
reject(err);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
}).on('error', reject);
|
|
342
388
|
});
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
389
|
+
|
|
390
|
+
const tarball = meta && meta.dist && meta.dist.tarball;
|
|
391
|
+
if (!tarball) {
|
|
392
|
+
spinner.fail('Could not determine tarball URL for update');
|
|
393
|
+
console.log(chalk.yellow(' You can manually update with: ') + chalk.bold.white('npm install -g vibecodingmachine-cli@latest\n'));
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
spinner.text = 'Resolving tarball...';
|
|
398
|
+
|
|
399
|
+
// HEAD to get content-length
|
|
400
|
+
const totalBytes = await new Promise((resolve) => {
|
|
401
|
+
const req = https.request(tarball, { method: 'HEAD' }, (res) => {
|
|
402
|
+
const len = parseInt(res.headers['content-length'] || '0', 10);
|
|
403
|
+
resolve(Number.isFinite(len) ? len : 0);
|
|
404
|
+
});
|
|
405
|
+
req.on('error', () => resolve(0));
|
|
406
|
+
req.end();
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
const tmpFile = path.join(os.tmpdir(), `vcm-update-${Date.now()}.tgz`);
|
|
410
|
+
|
|
411
|
+
// Download tarball with progress
|
|
412
|
+
spinner.text = 'Downloading update...';
|
|
413
|
+
// Stop the spinner so we can write a single-line progress indicator without conflicts
|
|
414
|
+
try { spinner.stop(); } catch (e) {}
|
|
415
|
+
// Print initial progress line (clear line first)
|
|
416
|
+
process.stdout.write('\r\x1b[2KDownloading: 0% â 0.0 MB');
|
|
417
|
+
const progressStart = Date.now();
|
|
418
|
+
function formatEta(sec) {
|
|
419
|
+
if (!isFinite(sec) || sec === null) return '--:--';
|
|
420
|
+
const s = Math.max(0, Math.round(sec));
|
|
421
|
+
const m = Math.floor(s / 60);
|
|
422
|
+
const ss = s % 60;
|
|
423
|
+
return `${m}:${ss.toString().padStart(2, '0')}`;
|
|
424
|
+
}
|
|
425
|
+
await new Promise((resolve, reject) => {
|
|
426
|
+
https.get(tarball, (res) => {
|
|
427
|
+
const fileStream = fs.createWriteStream(tmpFile);
|
|
428
|
+
let downloaded = 0;
|
|
429
|
+
|
|
430
|
+
// Print an updating single-line progress indicator (percent + MB)
|
|
431
|
+
let lastPercent = -1;
|
|
432
|
+
let lastMbReported = -1;
|
|
433
|
+
res.on('data', (chunk) => {
|
|
434
|
+
downloaded += chunk.length;
|
|
435
|
+
const percent = totalBytes ? Math.round((downloaded / totalBytes) * 100) : null;
|
|
436
|
+
const mbDownloaded = +(downloaded / (1024 * 1024));
|
|
437
|
+
const mbTotal = totalBytes ? (totalBytes / (1024 * 1024)) : null;
|
|
438
|
+
|
|
439
|
+
// ETA and speed
|
|
440
|
+
const elapsedSec = Math.max(0.001, (Date.now() - progressStart) / 1000);
|
|
441
|
+
const speed = downloaded / elapsedSec; // bytes/sec
|
|
442
|
+
const remaining = totalBytes ? Math.max(0, totalBytes - downloaded) : null;
|
|
443
|
+
const etaSec = remaining && speed > 0 ? remaining / speed : null;
|
|
444
|
+
|
|
445
|
+
// Build simple ASCII progress bar
|
|
446
|
+
const width = 30;
|
|
447
|
+
const fill = percent !== null ? Math.round((percent / 100) * width) : Math.min(width, Math.max(0, Math.round((mbDownloaded / (mbTotal || 1)) * width)));
|
|
448
|
+
const bar = 'â'.repeat(fill) + '-'.repeat(Math.max(0, width - fill));
|
|
449
|
+
|
|
450
|
+
const pctText = percent !== null ? `${percent}%` : '--%';
|
|
451
|
+
const mbText = mbTotal ? `${mbDownloaded.toFixed(1)} MB / ${mbTotal.toFixed(1)} MB` : `${mbDownloaded.toFixed(1)} MB`;
|
|
452
|
+
const etaText = etaSec ? formatEta(etaSec) : '--:--';
|
|
453
|
+
|
|
454
|
+
// Update only when percent changes or every 0.5 MB to reduce noise
|
|
455
|
+
const mbReport = Math.floor(mbDownloaded * 2) / 2;
|
|
456
|
+
if ((percent !== null && percent !== lastPercent) || (percent === null && mbReport !== lastMbReported) || Math.random() < 0.001) {
|
|
457
|
+
lastPercent = percent;
|
|
458
|
+
lastMbReported = mbReport;
|
|
459
|
+
process.stdout.write(`\r\x1b[2K[${bar}] ${pctText} ${mbText} ETA: ${etaText}`);
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
res.on('end', () => fileStream.end());
|
|
464
|
+
res.on('error', (err) => reject(err));
|
|
465
|
+
|
|
466
|
+
fileStream.on('finish', () => resolve());
|
|
467
|
+
fileStream.on('error', (err) => reject(err));
|
|
468
|
+
|
|
469
|
+
res.pipe(fileStream);
|
|
470
|
+
}).on('error', reject);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// Ensure progress line ends and move to next line
|
|
474
|
+
process.stdout.write('\n');
|
|
475
|
+
spinner.start();
|
|
476
|
+
spinner.succeed('Downloaded update');
|
|
477
|
+
|
|
478
|
+
// Install the downloaded tarball
|
|
479
|
+
spinner.start('Installing update...');
|
|
480
|
+
try {
|
|
481
|
+
execSync(`npm install -g "${tmpFile}"`, { stdio: 'inherit', encoding: 'utf8' });
|
|
482
|
+
spinner.succeed('Installed update');
|
|
483
|
+
console.log(chalk.green('\nâ
Successfully updated to v' + updateInfo.latestVersion + '!'));
|
|
484
|
+
console.log(chalk.gray(' Please restart your command to use the new version.\n'));
|
|
485
|
+
// Cleanup
|
|
486
|
+
try { fs.unlinkSync(tmpFile); } catch (e) {}
|
|
487
|
+
process.exit(0);
|
|
488
|
+
} catch (err) {
|
|
489
|
+
spinner.fail('Installation failed');
|
|
490
|
+
console.log(chalk.red('\nâ Update failed:'), err.message);
|
|
491
|
+
console.log(chalk.yellow(' You can manually update with: ') + chalk.bold.white('npm install -g vibecodingmachine-cli@latest\n'));
|
|
492
|
+
try { fs.unlinkSync(tmpFile); } catch (e) {}
|
|
493
|
+
}
|
|
346
494
|
} catch (error) {
|
|
347
495
|
console.log(chalk.red('\nâ Update failed:'), error.message);
|
|
348
496
|
console.log(chalk.yellow(' You can manually update with: ') + chalk.bold.white('npm install -g vibecodingmachine-cli@latest\n'));
|
|
@@ -384,6 +532,18 @@ if (!process.argv.slice(2).length) {
|
|
|
384
532
|
}
|
|
385
533
|
}
|
|
386
534
|
|
|
535
|
+
// Check compliance after authentication
|
|
536
|
+
const { checkCompliance } = require('../src/utils/compliance-check');
|
|
537
|
+
const isCompliant = await checkCompliance();
|
|
538
|
+
|
|
539
|
+
if (!isCompliant) {
|
|
540
|
+
console.log(chalk.red('\nâ Compliance check failed. Exiting.\n'));
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Track CLI usage - interactive mode
|
|
545
|
+
await auth.trackCLIActivity('interactive_mode_started');
|
|
546
|
+
|
|
387
547
|
const { startInteractive } = require('../src/utils/interactive');
|
|
388
548
|
await startInteractive();
|
|
389
549
|
})();
|
|
@@ -391,6 +551,47 @@ if (!process.argv.slice(2).length) {
|
|
|
391
551
|
// Check for updates before parsing commands
|
|
392
552
|
(async () => {
|
|
393
553
|
await checkForUpdates();
|
|
554
|
+
|
|
555
|
+
// Check authentication for all commands (except auth commands)
|
|
556
|
+
const command = process.argv[2] || 'unknown';
|
|
557
|
+
const authCommands = ['auth:login', 'auth:logout', 'auth:status'];
|
|
558
|
+
const skipAuthCheck = authCommands.includes(command);
|
|
559
|
+
|
|
560
|
+
const auth = require('../src/utils/auth');
|
|
561
|
+
|
|
562
|
+
if (!skipAuthCheck) {
|
|
563
|
+
const isAuth = await auth.isAuthenticated();
|
|
564
|
+
|
|
565
|
+
if (!isAuth) {
|
|
566
|
+
console.log(chalk.cyan('\nđ Opening browser for authentication...\n'));
|
|
567
|
+
try {
|
|
568
|
+
await auth.login();
|
|
569
|
+
console.log(chalk.green('\nâ Authentication successful!\n'));
|
|
570
|
+
} catch (error) {
|
|
571
|
+
console.log(chalk.red('\nâ Authentication failed:'), error.message);
|
|
572
|
+
process.exit(1);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Check compliance after authentication (skip for auth commands)
|
|
578
|
+
if (!skipAuthCheck) {
|
|
579
|
+
const { checkCompliance } = require('../src/utils/compliance-check');
|
|
580
|
+
const isCompliant = await checkCompliance();
|
|
581
|
+
|
|
582
|
+
if (!isCompliant) {
|
|
583
|
+
console.log(chalk.red('\nâ Compliance check failed. Exiting.\n'));
|
|
584
|
+
process.exit(1);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Track CLI usage - command execution
|
|
588
|
+
const args = process.argv.slice(3);
|
|
589
|
+
await auth.trackCLIActivity('command_executed', {
|
|
590
|
+
command,
|
|
591
|
+
args: args.filter(arg => !arg.includes('password') && !arg.includes('token'))
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
|
|
394
595
|
// Parse arguments only if commands were provided
|
|
395
596
|
program.parse(process.argv);
|
|
396
597
|
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibecodingmachine-cli",
|
|
3
|
-
"version": "2025.12.
|
|
3
|
+
"version": "2025.12.22-2230",
|
|
4
4
|
"description": "Command-line interface for Vibe Coding Machine - Autonomous development",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"author": "Vibe Coding Machine Team",
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"vibecodingmachine-core": "^2025.12.
|
|
28
|
+
"vibecodingmachine-core": "^2025.12.22-2230",
|
|
29
29
|
"@aws-sdk/client-dynamodb": "^3.600.0",
|
|
30
30
|
"@aws-sdk/lib-dynamodb": "^3.600.0",
|
|
31
31
|
"boxen": "^5.1.2",
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { getRequirementsPath } = require('vibecodingmachine-core');
|
|
4
|
+
const { getRepoPath } = require('./src/utils/config');
|
|
5
|
+
const requirements = require('./src/commands/requirements');
|
|
6
|
+
|
|
7
|
+
// Mock specific functions we need from interactive.js's logic
|
|
8
|
+
// helping function to move requirement to recycled (deletion logic)
|
|
9
|
+
async function moveRequirementToRecycled(reqPath, requirementTitle, fromSection) {
|
|
10
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
11
|
+
const lines = content.split('\n');
|
|
12
|
+
|
|
13
|
+
let requirementStartIndex = -1;
|
|
14
|
+
let requirementEndIndex = -1;
|
|
15
|
+
|
|
16
|
+
for (let i = 0; i < lines.length; i++) {
|
|
17
|
+
const line = lines[i].trim();
|
|
18
|
+
if (line.startsWith('###')) {
|
|
19
|
+
const title = line.replace(/^###\s*/, '').trim();
|
|
20
|
+
// Logic from interactive.js
|
|
21
|
+
if (title && title.includes(requirementTitle)) {
|
|
22
|
+
requirementStartIndex = i;
|
|
23
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
24
|
+
const nextLine = lines[j].trim();
|
|
25
|
+
if (nextLine.startsWith('###') || (nextLine.startsWith('##') && !nextLine.startsWith('###'))) {
|
|
26
|
+
requirementEndIndex = j;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (requirementEndIndex === -1) {
|
|
31
|
+
requirementEndIndex = lines.length;
|
|
32
|
+
}
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (requirementStartIndex === -1) {
|
|
39
|
+
console.log('â ď¸ Could not find requirement to recycle');
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log(`Found requirement at lines ${requirementStartIndex}-${requirementEndIndex}`);
|
|
44
|
+
|
|
45
|
+
const requirementBlock = lines.slice(requirementStartIndex, requirementEndIndex);
|
|
46
|
+
lines.splice(requirementStartIndex, requirementEndIndex - requirementStartIndex);
|
|
47
|
+
|
|
48
|
+
if (requirementStartIndex < lines.length) {
|
|
49
|
+
const nextLine = lines[requirementStartIndex]?.trim();
|
|
50
|
+
const packageNames = ['cli', 'core', 'electron-app', 'web', 'mobile', 'vscode-extension', 'sync-server'];
|
|
51
|
+
if (nextLine && packageNames.includes(nextLine.toLowerCase()) &&
|
|
52
|
+
!nextLine.startsWith('###') && !nextLine.startsWith('PACKAGE:')) {
|
|
53
|
+
lines.splice(requirementStartIndex, 1);
|
|
54
|
+
}
|
|
55
|
+
while (requirementStartIndex < lines.length && lines[requirementStartIndex]?.trim() === '') {
|
|
56
|
+
lines.splice(requirementStartIndex, 1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let recycledIndex = -1;
|
|
61
|
+
for (let i = 0; i < lines.length; i++) {
|
|
62
|
+
if (lines[i].includes('âťď¸ Recycled') || lines[i].includes('đď¸ Recycled')) {
|
|
63
|
+
recycledIndex = i;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (recycledIndex === -1) {
|
|
69
|
+
let lastSectionIndex = -1;
|
|
70
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
71
|
+
if (lines[i].startsWith('##') && !lines[i].startsWith('###')) {
|
|
72
|
+
lastSectionIndex = i;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const insertIndex = lastSectionIndex > 0 ? lastSectionIndex : lines.length;
|
|
77
|
+
lines.splice(insertIndex, 0, '', '## âťď¸ Recycled', '');
|
|
78
|
+
recycledIndex = insertIndex + 1;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let insertIndex = recycledIndex + 1;
|
|
82
|
+
while (insertIndex < lines.length && lines[insertIndex].trim() === '') {
|
|
83
|
+
insertIndex++;
|
|
84
|
+
}
|
|
85
|
+
lines.splice(insertIndex, 0, ...requirementBlock);
|
|
86
|
+
|
|
87
|
+
if (insertIndex + requirementBlock.length < lines.length && lines[insertIndex + requirementBlock.length].trim() !== '') {
|
|
88
|
+
lines.splice(insertIndex + requirementBlock.length, 0, '');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
await fs.writeFile(reqPath, lines.join('\n'));
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function run() {
|
|
96
|
+
try {
|
|
97
|
+
const repoPath = '/Users/jesse/code/mediawink/vibecodingmachine'; // Hardcoded valid repo path
|
|
98
|
+
console.log('Repo path:', repoPath);
|
|
99
|
+
|
|
100
|
+
const reqPath = await getRequirementsPath(repoPath);
|
|
101
|
+
console.log('<<< PATH >>>', reqPath);
|
|
102
|
+
|
|
103
|
+
if (await fs.pathExists(reqPath)) {
|
|
104
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
105
|
+
console.log('Current content length:', content.length);
|
|
106
|
+
console.log('Current content PRE-TEST:\n', content);
|
|
107
|
+
} else {
|
|
108
|
+
console.log('Requirements file does not exist');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
console.log('\n--- Adding TESTREQ1 ---');
|
|
112
|
+
await requirements.add('TESTREQ1', 'all', 'Description 1');
|
|
113
|
+
|
|
114
|
+
console.log('\n--- Adding TESTREQ2 ---');
|
|
115
|
+
await requirements.add('TESTREQ2', 'all', 'Description 2');
|
|
116
|
+
|
|
117
|
+
let content = await fs.readFile(reqPath, 'utf8');
|
|
118
|
+
console.log('Content after adding:\n', content);
|
|
119
|
+
|
|
120
|
+
// Verify order
|
|
121
|
+
// We expect TESTREQ2 to be above TESTREQ1 if it inserts at the top of the section
|
|
122
|
+
const lines = content.split('\n');
|
|
123
|
+
let idx1 = -1, idx2 = -1;
|
|
124
|
+
for (let i = 0; i < lines.length; i++) {
|
|
125
|
+
if (lines[i].includes('### TESTREQ1')) idx1 = i;
|
|
126
|
+
if (lines[i].includes('### TESTREQ2')) idx2 = i;
|
|
127
|
+
}
|
|
128
|
+
console.log(`TESTREQ1 line: ${idx1}, TESTREQ2 line: ${idx2}`);
|
|
129
|
+
if (idx2 < idx1 && idx2 > -1) {
|
|
130
|
+
console.log('SUCCESS: TESTREQ2 is above TESTREQ1 (Correct LIFO/Stack behavior for "Top of list")');
|
|
131
|
+
} else {
|
|
132
|
+
console.log('FAIL: Order is not correct for "Top of list" insertion');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log('\n--- Deleting TESTREQ1 ---');
|
|
136
|
+
const success = await moveRequirementToRecycled(reqPath, 'TESTREQ1', 'todo');
|
|
137
|
+
console.log('Delete success:', success);
|
|
138
|
+
|
|
139
|
+
content = await fs.readFile(reqPath, 'utf8');
|
|
140
|
+
console.log('Content after delete:\n', content);
|
|
141
|
+
|
|
142
|
+
if (content.includes('### TESTREQ1')) {
|
|
143
|
+
// It should be in recycled section
|
|
144
|
+
const recycledIndex = content.indexOf('## âťď¸ Recycled');
|
|
145
|
+
const reqIndex = content.indexOf('### TESTREQ1');
|
|
146
|
+
if (reqIndex > recycledIndex) {
|
|
147
|
+
console.log('SUCCESS: TESTREQ1 moved to Recycled');
|
|
148
|
+
} else {
|
|
149
|
+
console.log('FAIL: TESTREQ1 is still in TODO or wrong place');
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
console.log('FAIL: TESTREQ1 disappeared completely (should be recycled)');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error('Error:', error);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
run();
|
package/src/commands/auth.js
CHANGED
|
@@ -5,21 +5,29 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const chalk = require('chalk');
|
|
8
|
-
const { DirectLLMManager } = require('vibecodingmachine-core');
|
|
9
|
-
const { getRepoPath, getAutoConfig, setAutoConfig } = require('../utils/config');
|
|
8
|
+
const { DirectLLMManager, AppleScriptManager } = require('vibecodingmachine-core');
|
|
9
|
+
const { getRepoPath, getAutoConfig, setAutoConfig, getStages, DEFAULT_STAGES } = require('../utils/config');
|
|
10
10
|
const { getRequirementsPath, readRequirements } = require('vibecodingmachine-core');
|
|
11
11
|
const fs = require('fs-extra');
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const { spawn } = require('child_process');
|
|
14
14
|
const chokidar = require('chokidar');
|
|
15
|
+
const stringWidth = require('string-width');
|
|
16
|
+
const { getProviderPreferences, getProviderDefinition } = require('../utils/provider-registry');
|
|
17
|
+
const { createKeyboardHandler } = require('../utils/keyboard-handler');
|
|
18
|
+
const logger = require('../utils/logger');
|
|
19
|
+
const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
|
|
20
|
+
const { checkAntigravityRateLimit, handleAntigravityRateLimit } = require('../utils/antigravity-js-handler');
|
|
21
|
+
|
|
15
22
|
// Status management will use in-process tracking instead of external file
|
|
16
23
|
const CLI_ENTRY_POINT = path.join(__dirname, '../../bin/vibecodingmachine.js');
|
|
17
|
-
const { getProviderPreferences, getProviderDefinition } = require('../utils/provider-registry');
|
|
18
24
|
|
|
19
25
|
// CRITICAL: Shared ProviderManager instance to track rate limits across all function calls
|
|
20
|
-
const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
|
|
21
26
|
const sharedProviderManager = new ProviderManager();
|
|
22
27
|
|
|
28
|
+
// Configured stages (will be loaded in handleAutoStart)
|
|
29
|
+
let configuredStages = DEFAULT_STAGES;
|
|
30
|
+
|
|
23
31
|
/**
|
|
24
32
|
* Get timestamp for logging
|
|
25
33
|
*/
|
|
@@ -46,7 +54,6 @@ function stripAnsi(str) {
|
|
|
46
54
|
* Uses string-width library for accurate Unicode width calculation
|
|
47
55
|
*/
|
|
48
56
|
function getVisualWidth(str) {
|
|
49
|
-
const stringWidth = require('string-width');
|
|
50
57
|
return stringWidth(str);
|
|
51
58
|
}
|
|
52
59
|
|
|
@@ -111,7 +118,8 @@ async function updateRequirementsStatus(repoPath, status) {
|
|
|
111
118
|
break;
|
|
112
119
|
}
|
|
113
120
|
|
|
114
|
-
|
|
121
|
+
// Check against configured stages
|
|
122
|
+
if (inStatusSection && configuredStages.includes(line.trim())) {
|
|
115
123
|
statusLineIndex = i;
|
|
116
124
|
break;
|
|
117
125
|
}
|
|
@@ -160,14 +168,9 @@ let storedStatus = '';
|
|
|
160
168
|
* Now uses persistent header that stays at top while output scrolls
|
|
161
169
|
*/
|
|
162
170
|
function printStatusCard(currentTitle, currentStatus) {
|
|
163
|
-
const stages =
|
|
164
|
-
const stageMap = {
|
|
165
|
-
|
|
166
|
-
'ACT': 1,
|
|
167
|
-
'CLEAN UP': 2,
|
|
168
|
-
'VERIFY': 3,
|
|
169
|
-
'DONE': 4
|
|
170
|
-
};
|
|
171
|
+
const stages = configuredStages;
|
|
172
|
+
const stageMap = {};
|
|
173
|
+
stages.forEach((s, i) => stageMap[s] = i);
|
|
171
174
|
|
|
172
175
|
const currentIndex = stageMap[currentStatus] || 0;
|
|
173
176
|
|
|
@@ -249,7 +252,7 @@ async function getCurrentRequirement(repoPath) {
|
|
|
249
252
|
// Skip empty titles
|
|
250
253
|
if (title && title.length > 0) {
|
|
251
254
|
// Read package and description (optional)
|
|
252
|
-
let
|
|
255
|
+
let pkg = null;
|
|
253
256
|
let description = '';
|
|
254
257
|
let j = i + 1;
|
|
255
258
|
|
|
@@ -262,7 +265,7 @@ async function getCurrentRequirement(repoPath) {
|
|
|
262
265
|
}
|
|
263
266
|
// Check for PACKAGE line
|
|
264
267
|
if (nextLine.startsWith('PACKAGE:')) {
|
|
265
|
-
|
|
268
|
+
pkg = nextLine.replace(/^PACKAGE:\s*/, '').trim();
|
|
266
269
|
} else if (nextLine && !nextLine.startsWith('PACKAGE:')) {
|
|
267
270
|
// Description line (not empty, not package)
|
|
268
271
|
if (description) {
|
|
@@ -277,7 +280,7 @@ async function getCurrentRequirement(repoPath) {
|
|
|
277
280
|
return {
|
|
278
281
|
text: title,
|
|
279
282
|
fullLine: lines[i],
|
|
280
|
-
package:
|
|
283
|
+
package: pkg,
|
|
281
284
|
description: description
|
|
282
285
|
};
|
|
283
286
|
}
|
|
@@ -1334,11 +1337,14 @@ async function runIdeProviderIteration(providerConfig, repoPath) {
|
|
|
1334
1337
|
resolve({ success: true, output: combinedOutput });
|
|
1335
1338
|
} else {
|
|
1336
1339
|
const message = `${providerConfig.displayName} exited with code ${code}`;
|
|
1340
|
+
const antigravityRateLimit = checkAntigravityRateLimit(combinedOutput);
|
|
1341
|
+
|
|
1337
1342
|
resolve({
|
|
1338
1343
|
success: false,
|
|
1339
1344
|
error: combinedOutput ? `${message}\n${combinedOutput}` : message,
|
|
1340
1345
|
output: combinedOutput,
|
|
1341
|
-
rateLimited: isRateLimitMessage(combinedOutput)
|
|
1346
|
+
rateLimited: isRateLimitMessage(combinedOutput) || antigravityRateLimit.isRateLimited,
|
|
1347
|
+
antigravityRateLimited: antigravityRateLimit.isRateLimited
|
|
1342
1348
|
});
|
|
1343
1349
|
}
|
|
1344
1350
|
});
|
|
@@ -1435,26 +1441,15 @@ async function waitForIdeCompletion(repoPath, requirementText, ideType = '', tim
|
|
|
1435
1441
|
// Check 3: Quota limit detection for Antigravity (after 2 minutes of waiting)
|
|
1436
1442
|
const elapsed = Date.now() - startTime;
|
|
1437
1443
|
if (ideType === 'antigravity' && !quotaHandled && elapsed >= 120000) {
|
|
1438
|
-
console.log(chalk.yellow('\nâ ď¸
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
quotaHandled = true;
|
|
1448
|
-
// Reset start time to give new model time to work
|
|
1449
|
-
startTime = Date.now();
|
|
1450
|
-
} else {
|
|
1451
|
-
console.log(chalk.yellow(`â ď¸ Could not switch models: ${result.error}\n`));
|
|
1452
|
-
quotaHandled = true; // Don't try again
|
|
1453
|
-
}
|
|
1454
|
-
} catch (error) {
|
|
1455
|
-
console.error(chalk.red(`Error handling quota limit: ${error.message}\n`));
|
|
1456
|
-
quotaHandled = true; // Don't try again
|
|
1457
|
-
}
|
|
1444
|
+
console.log(chalk.yellow('\nâ ď¸ Antigravity quota limit detected after 2 minutes\n'));
|
|
1445
|
+
console.log(chalk.cyan(' Switching to next available IDE agent...\n'));
|
|
1446
|
+
watcher.close();
|
|
1447
|
+
resolve({
|
|
1448
|
+
success: false,
|
|
1449
|
+
reason: 'antigravity-quota',
|
|
1450
|
+
antigravityRateLimited: true // This triggers switching to next provider
|
|
1451
|
+
});
|
|
1452
|
+
return;
|
|
1458
1453
|
}
|
|
1459
1454
|
|
|
1460
1455
|
// Check 4: Timeout
|
|
@@ -1511,6 +1506,10 @@ async function runIdeFallbackIteration(requirement, providerConfig, repoPath, pr
|
|
|
1511
1506
|
const ideResult = await runIdeProviderIteration(providerConfig, repoPath);
|
|
1512
1507
|
|
|
1513
1508
|
if (!ideResult.success) {
|
|
1509
|
+
if (ideResult.antigravityRateLimited) {
|
|
1510
|
+
await handleAntigravityRateLimit();
|
|
1511
|
+
return { success: false, error: 'Antigravity rate limit detected, retrying with next provider.', shouldRetry: true };
|
|
1512
|
+
}
|
|
1514
1513
|
// CRITICAL: Mark provider as unavailable for ANY error so acquireProviderConfig() will skip it
|
|
1515
1514
|
providerManager.markRateLimited(providerConfig.provider, providerConfig.model, ideResult.output || ideResult.error || 'IDE provider failed');
|
|
1516
1515
|
return { success: false, error: ideResult.error || 'IDE provider failed' };
|
|
@@ -1522,6 +1521,12 @@ async function runIdeFallbackIteration(requirement, providerConfig, repoPath, pr
|
|
|
1522
1521
|
const completionResult = await waitForIdeCompletion(repoPath, requirement.text, providerConfig.ide || providerConfig.provider);
|
|
1523
1522
|
|
|
1524
1523
|
if (!completionResult.success) {
|
|
1524
|
+
if (completionResult.antigravityRateLimited) {
|
|
1525
|
+
console.log(chalk.yellow('â ď¸ Antigravity quota exhausted, switching to next IDE\n'));
|
|
1526
|
+
providerManager.markRateLimited(providerConfig.provider, providerConfig.model, 'Quota limit reached');
|
|
1527
|
+
return { success: false, error: 'Antigravity quota limit', shouldRetry: true };
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1525
1530
|
const errorMsg = completionResult.reason === 'timeout'
|
|
1526
1531
|
? 'IDE agent timed out'
|
|
1527
1532
|
: 'IDE agent failed to complete';
|
|
@@ -1891,6 +1896,9 @@ async function handleAutoStart(options) {
|
|
|
1891
1896
|
console.log();
|
|
1892
1897
|
|
|
1893
1898
|
// Get repo path
|
|
1899
|
+
// Load configured stages
|
|
1900
|
+
configuredStages = await getStages();
|
|
1901
|
+
|
|
1894
1902
|
const repoPath = await getRepoPath();
|
|
1895
1903
|
if (!repoPath) {
|
|
1896
1904
|
console.log(chalk.red('â No repository configured'));
|
|
@@ -1900,8 +1908,15 @@ async function handleAutoStart(options) {
|
|
|
1900
1908
|
|
|
1901
1909
|
console.log(chalk.white('Repository:'), chalk.cyan(repoPath));
|
|
1902
1910
|
|
|
1903
|
-
// Get
|
|
1904
|
-
|
|
1911
|
+
// Get effective agent using centralized selector
|
|
1912
|
+
const { getEffectiveAgent } = require('../utils/agent-selector');
|
|
1913
|
+
const PROVIDER_DEFINITIONS = require('./auto').PROVIDER_DEFINITIONS;
|
|
1914
|
+
const PROVIDER_DEFINITION_MAP = new Map(PROVIDER_DEFINITIONS.map(def => [def.id, def]));
|
|
1915
|
+
|
|
1916
|
+
const { effectiveAgent } = await getEffectiveAgent(options, PROVIDER_DEFINITIONS, PROVIDER_DEFINITION_MAP);
|
|
1917
|
+
|
|
1918
|
+
// Get provider configuration for the selected agent
|
|
1919
|
+
let providerConfig = await acquireProviderConfig(effectiveAgent);
|
|
1905
1920
|
if (!providerConfig) {
|
|
1906
1921
|
return;
|
|
1907
1922
|
}
|