vibecodingmachine-cli 2025.12.1-534 → 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 +301 -12
- package/package.json +5 -2
- package/repro_open.js +13 -0
- package/reproduce_issue.js +160 -0
- package/scripts/postinstall.js +80 -0
- package/src/commands/auth.js +0 -1
- package/src/commands/auto-direct.js +455 -136
- package/src/commands/auto.js +488 -163
- package/src/commands/computers.js +306 -0
- package/src/commands/repo.js +0 -1
- package/src/commands/requirements-remote.js +308 -0
- package/src/commands/requirements.js +233 -16
- package/src/commands/status.js +0 -1
- package/src/commands/sync.js +280 -0
- 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 +60 -0
- package/src/utils/auth.js +232 -8
- package/src/utils/auto-mode-ansi-ui.js +0 -1
- package/src/utils/auto-mode-simple-ui.js +3 -23
- 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 +410 -0
- package/src/utils/interactive.js +1197 -391
- package/src/utils/kiro-installer.js +178 -0
- package/src/utils/persistent-header.js +1 -3
- package/src/utils/provider-registry.js +13 -4
- package/src/utils/status-card.js +2 -1
- package/src/utils/user-tracking.js +300 -0
- package/tests/requirements-navigator-buildtree-await.test.js +28 -0
package/bin/vibecodingmachine.js
CHANGED
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
|
|
6
|
-
// Auto-load .env.
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
// Auto-load .env.cognito from root
|
|
7
|
+
const rootDir = path.join(__dirname, '..', '..', '..');
|
|
8
|
+
const envCognitoPath = path.join(rootDir, '.env.cognito');
|
|
9
|
+
|
|
10
|
+
if (fs.existsSync(envCognitoPath)) {
|
|
11
|
+
const envContent = fs.readFileSync(envCognitoPath, 'utf8');
|
|
10
12
|
envContent.split('\n').forEach(line => {
|
|
11
13
|
const trimmed = line.trim();
|
|
12
14
|
if (trimmed && !trimmed.startsWith('#')) {
|
|
@@ -23,10 +25,23 @@ if (fs.existsSync(envAuthPath)) {
|
|
|
23
25
|
* "Big Dreams + AI + VibeCodingMachine.com = Your money making apps"
|
|
24
26
|
*/
|
|
25
27
|
|
|
28
|
+
const { cleanupBrokenAssets } = require('../src/utils/asset-cleanup');
|
|
29
|
+
|
|
30
|
+
(async () => {
|
|
31
|
+
// mitigate broken assets issue
|
|
32
|
+
await cleanupBrokenAssets();
|
|
33
|
+
})();
|
|
34
|
+
|
|
26
35
|
const { program } = require('commander');
|
|
27
36
|
const chalk = require('chalk');
|
|
28
37
|
const inquirer = require('inquirer');
|
|
29
|
-
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);
|
|
30
45
|
// const path = require('path'); // Moved to top for .env.auth loading
|
|
31
46
|
const packageJson = require('../package.json');
|
|
32
47
|
|
|
@@ -108,6 +123,11 @@ program
|
|
|
108
123
|
.option('--no-never-stop', 'Disable never stop mode')
|
|
109
124
|
.action(autoCommands.config);
|
|
110
125
|
|
|
126
|
+
program
|
|
127
|
+
.command('auto:agents')
|
|
128
|
+
.description('List available agents and their quota status')
|
|
129
|
+
.action(autoCommands.listAgents);
|
|
130
|
+
|
|
111
131
|
// Requirements management commands
|
|
112
132
|
program
|
|
113
133
|
.command('req:list')
|
|
@@ -140,6 +160,11 @@ program
|
|
|
140
160
|
.description('Watch requirements file for changes')
|
|
141
161
|
.action(reqCommands.watch);
|
|
142
162
|
|
|
163
|
+
program
|
|
164
|
+
.command('req:rename <old-title> <new-title> [description...]')
|
|
165
|
+
.description('Rename requirement and optionally update description')
|
|
166
|
+
.action((oldTitle, newTitle, description) => reqCommands.rename(oldTitle, newTitle, description));
|
|
167
|
+
|
|
143
168
|
// IDE integration commands
|
|
144
169
|
program
|
|
145
170
|
.command('ide:list')
|
|
@@ -201,6 +226,76 @@ program
|
|
|
201
226
|
.description('Check authentication status')
|
|
202
227
|
.action(authCommands.status);
|
|
203
228
|
|
|
229
|
+
// Multi-computer management commands
|
|
230
|
+
const computerCommands = require('../src/commands/computers');
|
|
231
|
+
const remoteReqCommands = require('../src/commands/requirements-remote');
|
|
232
|
+
program
|
|
233
|
+
.command('computers')
|
|
234
|
+
.description('List all registered computers')
|
|
235
|
+
.option('-f, --focus <area>', 'Filter by focus area')
|
|
236
|
+
.option('-s, --status <status>', 'Filter by status (active, idle, error)')
|
|
237
|
+
.action(computerCommands.listComputers);
|
|
238
|
+
|
|
239
|
+
program
|
|
240
|
+
.command('computer:status <computerId>')
|
|
241
|
+
.description('Show detailed status of a specific computer')
|
|
242
|
+
.action(computerCommands.showComputerStatus);
|
|
243
|
+
|
|
244
|
+
program
|
|
245
|
+
.command('computer:register <focusArea>')
|
|
246
|
+
.description('Register current computer with focus area')
|
|
247
|
+
.action(computerCommands.registerComputer);
|
|
248
|
+
|
|
249
|
+
program
|
|
250
|
+
.command('computer:focus [newFocusArea]')
|
|
251
|
+
.description('View or update focus area for current computer')
|
|
252
|
+
.action((newFocusArea) => {
|
|
253
|
+
if (newFocusArea) {
|
|
254
|
+
computerCommands.updateFocus(newFocusArea);
|
|
255
|
+
} else {
|
|
256
|
+
// Show current focus
|
|
257
|
+
computerCommands.showComputerStatus(require('os').hostname());
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
program
|
|
262
|
+
.command('computer:requirements <computerId>')
|
|
263
|
+
.description('View requirements for another computer')
|
|
264
|
+
.action(remoteReqCommands.listRemoteRequirements);
|
|
265
|
+
|
|
266
|
+
program
|
|
267
|
+
.command('computer:add-requirement <computerId> <requirement>')
|
|
268
|
+
.description('Add requirement to another computer')
|
|
269
|
+
.action(remoteReqCommands.addRemoteRequirement);
|
|
270
|
+
|
|
271
|
+
// Sync management commands
|
|
272
|
+
const syncCommands = require('../src/commands/sync');
|
|
273
|
+
program
|
|
274
|
+
.command('sync:now')
|
|
275
|
+
.description('Trigger immediate sync')
|
|
276
|
+
.action(syncCommands.syncNow);
|
|
277
|
+
|
|
278
|
+
program
|
|
279
|
+
.command('sync:status')
|
|
280
|
+
.description('Show sync status and statistics')
|
|
281
|
+
.action(syncCommands.syncStatus);
|
|
282
|
+
|
|
283
|
+
program
|
|
284
|
+
.command('sync:queue')
|
|
285
|
+
.description('View pending changes in offline queue')
|
|
286
|
+
.action(syncCommands.viewQueue);
|
|
287
|
+
|
|
288
|
+
program
|
|
289
|
+
.command('sync:force')
|
|
290
|
+
.description('Force sync even if offline')
|
|
291
|
+
.action(syncCommands.forceSync);
|
|
292
|
+
|
|
293
|
+
program
|
|
294
|
+
.command('sync:history')
|
|
295
|
+
.description('View sync history')
|
|
296
|
+
.option('-n, --limit <number>', 'Number of history entries to show', '50')
|
|
297
|
+
.action(syncCommands.viewHistory);
|
|
298
|
+
|
|
204
299
|
// Interactive mode
|
|
205
300
|
program
|
|
206
301
|
.command('interactive')
|
|
@@ -214,18 +309,33 @@ program
|
|
|
214
309
|
// Error handling
|
|
215
310
|
process.on('uncaughtException', (error) => {
|
|
216
311
|
console.error(chalk.red('Error:'), error.message);
|
|
312
|
+
if (process.env.DEBUG) {
|
|
313
|
+
console.error(chalk.gray('Stack:'), error.stack);
|
|
314
|
+
}
|
|
217
315
|
process.exit(1);
|
|
218
316
|
});
|
|
219
317
|
|
|
220
318
|
process.on('unhandledRejection', (error) => {
|
|
221
319
|
console.error(chalk.red('Error:'), error.message);
|
|
320
|
+
if (process.env.DEBUG) {
|
|
321
|
+
console.error(chalk.gray('Stack:'), error.stack);
|
|
322
|
+
}
|
|
222
323
|
process.exit(1);
|
|
223
324
|
});
|
|
224
325
|
|
|
225
326
|
// Check for updates and display notification
|
|
226
327
|
async function checkForUpdates() {
|
|
227
328
|
try {
|
|
329
|
+
if (process.env.VCM_SKIP_UPDATE_CHECK === '1' || process.env.VCM_SKIP_UPDATE_CHECK === 'true') {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
228
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
|
+
}
|
|
229
339
|
console.log(chalk.gray(`\n🔍 Checking for updates... (current: v${packageJson.version})`));
|
|
230
340
|
const updateInfo = await checkForCLIUpdates(packageJson.version);
|
|
231
341
|
console.log(chalk.gray(` Update check result: ${JSON.stringify(updateInfo)}\n`));
|
|
@@ -251,14 +361,136 @@ async function checkForUpdates() {
|
|
|
251
361
|
if (answer.shouldUpdate) {
|
|
252
362
|
console.log(chalk.cyan('\n🔄 Updating VibeCodingMachine CLI...\n'));
|
|
253
363
|
try {
|
|
254
|
-
//
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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);
|
|
388
|
+
});
|
|
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();
|
|
258
407
|
});
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
+
}
|
|
262
494
|
} catch (error) {
|
|
263
495
|
console.log(chalk.red('\n❌ Update failed:'), error.message);
|
|
264
496
|
console.log(chalk.yellow(' You can manually update with: ') + chalk.bold.white('npm install -g vibecodingmachine-cli@latest\n'));
|
|
@@ -281,6 +513,10 @@ if (!process.argv.slice(2).length) {
|
|
|
281
513
|
// Check for updates first
|
|
282
514
|
await checkForUpdates();
|
|
283
515
|
|
|
516
|
+
// Check for first run experience
|
|
517
|
+
const { checkFirstRun } = require('../src/utils/first-run');
|
|
518
|
+
await checkFirstRun();
|
|
519
|
+
|
|
284
520
|
// Check authentication before allowing interactive mode
|
|
285
521
|
const auth = require('../src/utils/auth');
|
|
286
522
|
const isAuth = await auth.isAuthenticated();
|
|
@@ -296,6 +532,18 @@ if (!process.argv.slice(2).length) {
|
|
|
296
532
|
}
|
|
297
533
|
}
|
|
298
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
|
+
|
|
299
547
|
const { startInteractive } = require('../src/utils/interactive');
|
|
300
548
|
await startInteractive();
|
|
301
549
|
})();
|
|
@@ -303,6 +551,47 @@ if (!process.argv.slice(2).length) {
|
|
|
303
551
|
// Check for updates before parsing commands
|
|
304
552
|
(async () => {
|
|
305
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
|
+
|
|
306
595
|
// Parse arguments only if commands were provided
|
|
307
596
|
program.parse(process.argv);
|
|
308
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,10 +25,13 @@
|
|
|
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
|
+
"@aws-sdk/client-dynamodb": "^3.600.0",
|
|
30
|
+
"@aws-sdk/lib-dynamodb": "^3.600.0",
|
|
29
31
|
"boxen": "^5.1.2",
|
|
30
32
|
"chalk": "^4.1.2",
|
|
31
33
|
"chokidar": "^3.6.0",
|
|
34
|
+
"cli-table3": "^0.6.3",
|
|
32
35
|
"commander": "^11.1.0",
|
|
33
36
|
"fs-extra": "^11.2.0",
|
|
34
37
|
"ink": "^6.4.0",
|
package/repro_open.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
try {
|
|
2
|
+
const open = require('open');
|
|
3
|
+
console.log('Type of open:', typeof open);
|
|
4
|
+
console.log('open:', open);
|
|
5
|
+
if (typeof open !== 'function') {
|
|
6
|
+
console.log('Exports:', Object.keys(open));
|
|
7
|
+
if (open.default) {
|
|
8
|
+
console.log('Type of open.default:', typeof open.default);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
} catch (e) {
|
|
12
|
+
console.error('Require failed:', e.message);
|
|
13
|
+
}
|
|
@@ -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();
|