vibecodingmachine-cli 2025.12.6-1702 → 2025.12.24-2348
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/vibecodingmachine.js +260 -52
- package/logs/audit/2025-12-24.jsonl +2 -0
- package/package.json +2 -2
- package/reproduce_issue.js +160 -0
- package/src/commands/auth.js +19 -19
- package/src/commands/auto-direct.js +133 -93
- package/src/commands/auto.js +165 -67
- package/src/commands/computers.js +62 -59
- package/src/commands/locale.js +73 -0
- package/src/commands/repo.js +0 -1
- package/src/commands/requirements-remote.js +14 -9
- package/src/commands/requirements.js +29 -3
- package/src/commands/setup.js +14 -13
- package/src/commands/status.js +17 -18
- package/src/commands/sync.js +72 -71
- package/src/utils/agent-selector.js +50 -0
- package/src/utils/antigravity-installer.js +212 -0
- package/src/utils/antigravity-js-handler.js +60 -0
- package/src/utils/asset-cleanup.js +0 -1
- package/src/utils/auth.js +149 -2
- package/src/utils/auto-mode-ansi-ui.js +0 -1
- package/src/utils/auto-mode-simple-ui.js +1 -1
- package/src/utils/compliance-check.js +166 -0
- package/src/utils/config.js +27 -1
- package/src/utils/copy-with-progress.js +167 -0
- package/src/utils/download-with-progress.js +84 -0
- package/src/utils/first-run.js +189 -71
- package/src/utils/interactive.js +419 -362
- package/src/utils/kiro-installer.js +56 -24
- package/src/utils/persistent-header.js +1 -3
- package/src/utils/provider-registry.js +43 -5
- package/src/utils/user-tracking.js +300 -0
- package/tests/requirements-navigator-buildtree-await.test.js +28 -0
package/bin/vibecodingmachine.js
CHANGED
|
@@ -35,10 +35,23 @@ 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
|
|
|
48
|
+
// Import localization
|
|
49
|
+
const { t, detectLocale, setLocale } = require('vibecodingmachine-core');
|
|
50
|
+
|
|
51
|
+
// Initialize locale detection
|
|
52
|
+
const detectedLocale = detectLocale();
|
|
53
|
+
setLocale(detectedLocale);
|
|
54
|
+
|
|
42
55
|
// Import command modules
|
|
43
56
|
const repoCommands = require('../src/commands/repo');
|
|
44
57
|
const autoCommands = require('../src/commands/auto');
|
|
@@ -54,37 +67,37 @@ const altCommandName = commandName === 'vcm' ? 'vibecodingmachine' : 'vcm';
|
|
|
54
67
|
program
|
|
55
68
|
.name(commandName)
|
|
56
69
|
.usage(`[options] [command]\n\n You can use '${commandName}' or '${altCommandName}'`)
|
|
57
|
-
.description('
|
|
70
|
+
.description(t('app.name') + ' CLI - Autonomous development')
|
|
58
71
|
.version(packageJson.version, '-v, --version', 'output the current version')
|
|
59
72
|
.helpOption('-h, --help', 'display help for command');
|
|
60
73
|
|
|
61
74
|
// Repository management commands
|
|
62
75
|
program
|
|
63
76
|
.command('repo')
|
|
64
|
-
.description('
|
|
77
|
+
.description(t('cli.repo.manage'))
|
|
65
78
|
.action(() => {
|
|
66
79
|
program.outputHelp();
|
|
67
80
|
});
|
|
68
81
|
|
|
69
82
|
program
|
|
70
83
|
.command('repo:set <path>')
|
|
71
|
-
.description('
|
|
84
|
+
.description(t('cli.repo.set'))
|
|
72
85
|
.action(repoCommands.setRepo);
|
|
73
86
|
|
|
74
87
|
program
|
|
75
88
|
.command('repo:get')
|
|
76
|
-
.description('
|
|
89
|
+
.description(t('cli.repo.get'))
|
|
77
90
|
.action(repoCommands.getRepo);
|
|
78
91
|
|
|
79
92
|
program
|
|
80
93
|
.command('repo:init')
|
|
81
|
-
.description('
|
|
94
|
+
.description(t('cli.repo.init'))
|
|
82
95
|
.action(repoCommands.initRepo);
|
|
83
96
|
|
|
84
97
|
// Auto mode commands
|
|
85
98
|
program
|
|
86
99
|
.command('auto:start')
|
|
87
|
-
.description('
|
|
100
|
+
.description(t('cli.auto.start'))
|
|
88
101
|
.option('-i, --ide <ide>', 'IDE to use (claude-code, aider, cursor, vscode, windsurf, cline)')
|
|
89
102
|
.option('-m, --max-chats <number>', 'Maximum number of chat iterations', parseInt)
|
|
90
103
|
.option('-n, --never-stop', 'Run indefinitely without stopping')
|
|
@@ -95,103 +108,108 @@ program
|
|
|
95
108
|
const { handleAutoStart: handleDirectAutoStart } = require('../src/commands/auto-direct');
|
|
96
109
|
program
|
|
97
110
|
.command('auto:direct')
|
|
98
|
-
.description('
|
|
111
|
+
.description(t('cli.auto.direct'))
|
|
99
112
|
.option('-m, --max-chats <number>', 'Maximum number of iterations', parseInt)
|
|
100
113
|
.action(handleDirectAutoStart);
|
|
101
114
|
|
|
102
115
|
program
|
|
103
116
|
.command('auto:stop')
|
|
104
|
-
.description('
|
|
117
|
+
.description(t('cli.auto.stop'))
|
|
105
118
|
.action(autoCommands.stop);
|
|
106
119
|
|
|
107
120
|
program
|
|
108
121
|
.command('auto:status')
|
|
109
|
-
.description('
|
|
122
|
+
.description(t('cli.auto.status'))
|
|
110
123
|
.action(autoCommands.status);
|
|
111
124
|
|
|
112
125
|
program
|
|
113
126
|
.command('auto:config')
|
|
114
|
-
.description('
|
|
127
|
+
.description(t('cli.auto.config'))
|
|
115
128
|
.option('--max-chats <number>', 'Set maximum chat iterations', parseInt)
|
|
116
129
|
.option('--never-stop', 'Enable never stop mode')
|
|
117
130
|
.option('--no-never-stop', 'Disable never stop mode')
|
|
118
131
|
.action(autoCommands.config);
|
|
119
132
|
|
|
133
|
+
program
|
|
134
|
+
.command('auto:agents')
|
|
135
|
+
.description(t('cli.auto.agents'))
|
|
136
|
+
.action(autoCommands.listAgents);
|
|
137
|
+
|
|
120
138
|
// Requirements management commands
|
|
121
139
|
program
|
|
122
140
|
.command('req:list')
|
|
123
|
-
.description('
|
|
141
|
+
.description(t('cli.req.list'))
|
|
124
142
|
.option('-s, --status <status>', 'Filter by status (pending, in-progress, completed)')
|
|
125
143
|
.action(reqCommands.list);
|
|
126
144
|
|
|
127
145
|
program
|
|
128
146
|
.command('req:add <requirement>')
|
|
129
|
-
.description('
|
|
147
|
+
.description(t('cli.req.add'))
|
|
130
148
|
.action(reqCommands.add);
|
|
131
149
|
|
|
132
150
|
program
|
|
133
151
|
.command('req:current')
|
|
134
|
-
.description('
|
|
152
|
+
.description(t('cli.req.current'))
|
|
135
153
|
.action(reqCommands.current);
|
|
136
154
|
|
|
137
155
|
program
|
|
138
156
|
.command('req:next')
|
|
139
|
-
.description('
|
|
157
|
+
.description(t('cli.req.next'))
|
|
140
158
|
.action(reqCommands.next);
|
|
141
159
|
|
|
142
160
|
program
|
|
143
161
|
.command('req:edit')
|
|
144
|
-
.description('
|
|
162
|
+
.description(t('cli.req.edit'))
|
|
145
163
|
.action(reqCommands.edit);
|
|
146
164
|
|
|
147
165
|
program
|
|
148
166
|
.command('req:watch')
|
|
149
|
-
.description('
|
|
167
|
+
.description(t('cli.req.watch'))
|
|
150
168
|
.action(reqCommands.watch);
|
|
151
169
|
|
|
152
170
|
program
|
|
153
171
|
.command('req:rename <old-title> <new-title> [description...]')
|
|
154
|
-
.description('
|
|
172
|
+
.description(t('cli.req.rename'))
|
|
155
173
|
.action((oldTitle, newTitle, description) => reqCommands.rename(oldTitle, newTitle, description));
|
|
156
174
|
|
|
157
175
|
// IDE integration commands
|
|
158
176
|
program
|
|
159
177
|
.command('ide:list')
|
|
160
|
-
.description('
|
|
178
|
+
.description(t('cli.ide.list'))
|
|
161
179
|
.action(ideCommands.list);
|
|
162
180
|
|
|
163
181
|
program
|
|
164
182
|
.command('ide:open <ide>')
|
|
165
|
-
.description('
|
|
183
|
+
.description(t('cli.ide.open'))
|
|
166
184
|
.action(ideCommands.open);
|
|
167
185
|
|
|
168
186
|
program
|
|
169
187
|
.command('ide:send <message>')
|
|
170
|
-
.description('
|
|
188
|
+
.description(t('cli.ide.send'))
|
|
171
189
|
.option('-i, --ide <ide>', 'IDE to use (cursor, vscode, windsurf)', 'cursor')
|
|
172
190
|
.action(ideCommands.send);
|
|
173
191
|
|
|
174
192
|
// Status and monitoring commands
|
|
175
193
|
program
|
|
176
194
|
.command('status')
|
|
177
|
-
.description('
|
|
195
|
+
.description(t('cli.status'))
|
|
178
196
|
.action(statusCommands.show);
|
|
179
197
|
|
|
180
198
|
program
|
|
181
199
|
.command('progress')
|
|
182
|
-
.description('
|
|
200
|
+
.description(t('cli.progress'))
|
|
183
201
|
.action(statusCommands.progress);
|
|
184
202
|
|
|
185
203
|
program
|
|
186
204
|
.command('logs')
|
|
187
|
-
.description('
|
|
205
|
+
.description(t('cli.logs'))
|
|
188
206
|
.option('-n, --lines <number>', 'Number of log lines to show', '50')
|
|
189
207
|
.action(statusCommands.logs);
|
|
190
208
|
|
|
191
209
|
// Setup command
|
|
192
210
|
program
|
|
193
211
|
.command('setup')
|
|
194
|
-
.description('
|
|
212
|
+
.description(t('cli.setup'))
|
|
195
213
|
.action(async () => {
|
|
196
214
|
const { setupAlias } = require('../src/commands/setup');
|
|
197
215
|
await setupAlias();
|
|
@@ -201,18 +219,18 @@ program
|
|
|
201
219
|
const authCommands = require('../src/commands/auth');
|
|
202
220
|
program
|
|
203
221
|
.command('auth:login')
|
|
204
|
-
.description('
|
|
222
|
+
.description(t('cli.auth.login'))
|
|
205
223
|
.option('--headless', 'Use headless mode (manual URL paste) for SSH/no-GUI environments')
|
|
206
224
|
.action((options) => authCommands.login(options));
|
|
207
225
|
|
|
208
226
|
program
|
|
209
227
|
.command('auth:logout')
|
|
210
|
-
.description('
|
|
228
|
+
.description(t('cli.auth.logout'))
|
|
211
229
|
.action(authCommands.logout);
|
|
212
230
|
|
|
213
231
|
program
|
|
214
232
|
.command('auth:status')
|
|
215
|
-
.description('
|
|
233
|
+
.description(t('cli.auth.status'))
|
|
216
234
|
.action(authCommands.status);
|
|
217
235
|
|
|
218
236
|
// Multi-computer management commands
|
|
@@ -220,24 +238,24 @@ const computerCommands = require('../src/commands/computers');
|
|
|
220
238
|
const remoteReqCommands = require('../src/commands/requirements-remote');
|
|
221
239
|
program
|
|
222
240
|
.command('computers')
|
|
223
|
-
.description('
|
|
241
|
+
.description(t('cli.computers.list'))
|
|
224
242
|
.option('-f, --focus <area>', 'Filter by focus area')
|
|
225
243
|
.option('-s, --status <status>', 'Filter by status (active, idle, error)')
|
|
226
244
|
.action(computerCommands.listComputers);
|
|
227
245
|
|
|
228
246
|
program
|
|
229
247
|
.command('computer:status <computerId>')
|
|
230
|
-
.description('
|
|
248
|
+
.description(t('cli.computer.status.show'))
|
|
231
249
|
.action(computerCommands.showComputerStatus);
|
|
232
250
|
|
|
233
251
|
program
|
|
234
252
|
.command('computer:register <focusArea>')
|
|
235
|
-
.description('
|
|
253
|
+
.description(t('cli.computer.register'))
|
|
236
254
|
.action(computerCommands.registerComputer);
|
|
237
255
|
|
|
238
256
|
program
|
|
239
257
|
.command('computer:focus [newFocusArea]')
|
|
240
|
-
.description('
|
|
258
|
+
.description(t('cli.computer.focus.view'))
|
|
241
259
|
.action((newFocusArea) => {
|
|
242
260
|
if (newFocusArea) {
|
|
243
261
|
computerCommands.updateFocus(newFocusArea);
|
|
@@ -249,39 +267,39 @@ program
|
|
|
249
267
|
|
|
250
268
|
program
|
|
251
269
|
.command('computer:requirements <computerId>')
|
|
252
|
-
.description('
|
|
270
|
+
.description(t('cli.computer.requirements.view'))
|
|
253
271
|
.action(remoteReqCommands.listRemoteRequirements);
|
|
254
272
|
|
|
255
273
|
program
|
|
256
274
|
.command('computer:add-requirement <computerId> <requirement>')
|
|
257
|
-
.description('
|
|
275
|
+
.description(t('cli.computer.requirement.add'))
|
|
258
276
|
.action(remoteReqCommands.addRemoteRequirement);
|
|
259
277
|
|
|
260
278
|
// Sync management commands
|
|
261
279
|
const syncCommands = require('../src/commands/sync');
|
|
262
280
|
program
|
|
263
281
|
.command('sync:now')
|
|
264
|
-
.description('
|
|
282
|
+
.description(t('cli.sync.trigger'))
|
|
265
283
|
.action(syncCommands.syncNow);
|
|
266
284
|
|
|
267
285
|
program
|
|
268
286
|
.command('sync:status')
|
|
269
|
-
.description('
|
|
287
|
+
.description(t('cli.sync.status.show'))
|
|
270
288
|
.action(syncCommands.syncStatus);
|
|
271
289
|
|
|
272
290
|
program
|
|
273
291
|
.command('sync:queue')
|
|
274
|
-
.description('
|
|
292
|
+
.description(t('cli.sync.queue.view'))
|
|
275
293
|
.action(syncCommands.viewQueue);
|
|
276
294
|
|
|
277
295
|
program
|
|
278
296
|
.command('sync:force')
|
|
279
|
-
.description('
|
|
297
|
+
.description(t('cli.sync.force.sync'))
|
|
280
298
|
.action(syncCommands.forceSync);
|
|
281
299
|
|
|
282
300
|
program
|
|
283
301
|
.command('sync:history')
|
|
284
|
-
.description('
|
|
302
|
+
.description(t('cli.sync.history.view'))
|
|
285
303
|
.option('-n, --limit <number>', 'Number of history entries to show', '50')
|
|
286
304
|
.action(syncCommands.viewHistory);
|
|
287
305
|
|
|
@@ -289,7 +307,7 @@ program
|
|
|
289
307
|
program
|
|
290
308
|
.command('interactive')
|
|
291
309
|
.alias('i')
|
|
292
|
-
.description('
|
|
310
|
+
.description(t('cli.interactive'))
|
|
293
311
|
.action(async () => {
|
|
294
312
|
const { startInteractive } = require('../src/utils/interactive');
|
|
295
313
|
await startInteractive();
|
|
@@ -297,19 +315,34 @@ program
|
|
|
297
315
|
|
|
298
316
|
// Error handling
|
|
299
317
|
process.on('uncaughtException', (error) => {
|
|
300
|
-
console.error(chalk.red('
|
|
318
|
+
console.error(chalk.red(`${t('cli.error')}:`), error.message);
|
|
319
|
+
if (process.env.DEBUG) {
|
|
320
|
+
console.error(chalk.gray('Stack:'), error.stack);
|
|
321
|
+
}
|
|
301
322
|
process.exit(1);
|
|
302
323
|
});
|
|
303
324
|
|
|
304
325
|
process.on('unhandledRejection', (error) => {
|
|
305
|
-
console.error(chalk.red('
|
|
326
|
+
console.error(chalk.red(`${t('cli.error')}:`), error.message);
|
|
327
|
+
if (process.env.DEBUG) {
|
|
328
|
+
console.error(chalk.gray('Stack:'), error.stack);
|
|
329
|
+
}
|
|
306
330
|
process.exit(1);
|
|
307
331
|
});
|
|
308
332
|
|
|
309
333
|
// Check for updates and display notification
|
|
310
334
|
async function checkForUpdates() {
|
|
311
335
|
try {
|
|
336
|
+
if (process.env.VCM_SKIP_UPDATE_CHECK === '1' || process.env.VCM_SKIP_UPDATE_CHECK === 'true') {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
312
339
|
const { checkForCLIUpdates } = require('vibecodingmachine-core');
|
|
340
|
+
// If running inside the repository (local development), skip update checks entirely
|
|
341
|
+
const isDevWorkspace = fs.existsSync(path.join(rootDir, '.git'));
|
|
342
|
+
if (isDevWorkspace) {
|
|
343
|
+
console.log(chalk.yellow('\nDetected local development workspace; skipping update check.'));
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
313
346
|
console.log(chalk.gray(`\n🔍 Checking for updates... (current: v${packageJson.version})`));
|
|
314
347
|
const updateInfo = await checkForCLIUpdates(packageJson.version);
|
|
315
348
|
console.log(chalk.gray(` Update check result: ${JSON.stringify(updateInfo)}\n`));
|
|
@@ -335,14 +368,136 @@ async function checkForUpdates() {
|
|
|
335
368
|
if (answer.shouldUpdate) {
|
|
336
369
|
console.log(chalk.cyan('\n🔄 Updating VibeCodingMachine CLI...\n'));
|
|
337
370
|
try {
|
|
338
|
-
//
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
371
|
+
// If we're running from the repository (local dev), avoid performing a global install
|
|
372
|
+
const isDevWorkspace = fs.existsSync(path.join(rootDir, '.git'));
|
|
373
|
+
if (isDevWorkspace) {
|
|
374
|
+
console.log(chalk.yellow('\nDetected local development workspace; skipping automatic global install.'));
|
|
375
|
+
console.log(chalk.gray(' To update the globally installed CLI run:') + ' ' + chalk.bold.white('npm install -g vibecodingmachine-cli@latest\n'));
|
|
376
|
+
// Do not attempt global install during development to avoid confusing local dev flow
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const spinner = ora('Fetching package metadata...').start();
|
|
380
|
+
|
|
381
|
+
// Get latest package metadata from npm registry
|
|
382
|
+
const registryUrl = 'https://registry.npmjs.org/vibecodingmachine-cli/latest';
|
|
383
|
+
const meta = await new Promise((resolve, reject) => {
|
|
384
|
+
https.get(registryUrl, (res) => {
|
|
385
|
+
let data = '';
|
|
386
|
+
res.on('data', (chunk) => data += chunk);
|
|
387
|
+
res.on('end', () => {
|
|
388
|
+
try {
|
|
389
|
+
resolve(JSON.parse(data));
|
|
390
|
+
} catch (err) {
|
|
391
|
+
reject(err);
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
}).on('error', reject);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
const tarball = meta && meta.dist && meta.dist.tarball;
|
|
398
|
+
if (!tarball) {
|
|
399
|
+
spinner.fail('Could not determine tarball URL for update');
|
|
400
|
+
console.log(chalk.yellow(' You can manually update with: ') + chalk.bold.white('npm install -g vibecodingmachine-cli@latest\n'));
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
spinner.text = 'Resolving tarball...';
|
|
405
|
+
|
|
406
|
+
// HEAD to get content-length
|
|
407
|
+
const totalBytes = await new Promise((resolve) => {
|
|
408
|
+
const req = https.request(tarball, { method: 'HEAD' }, (res) => {
|
|
409
|
+
const len = parseInt(res.headers['content-length'] || '0', 10);
|
|
410
|
+
resolve(Number.isFinite(len) ? len : 0);
|
|
411
|
+
});
|
|
412
|
+
req.on('error', () => resolve(0));
|
|
413
|
+
req.end();
|
|
342
414
|
});
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
415
|
+
|
|
416
|
+
const tmpFile = path.join(os.tmpdir(), `vcm-update-${Date.now()}.tgz`);
|
|
417
|
+
|
|
418
|
+
// Download tarball with progress
|
|
419
|
+
spinner.text = 'Downloading update...';
|
|
420
|
+
// Stop the spinner so we can write a single-line progress indicator without conflicts
|
|
421
|
+
try { spinner.stop(); } catch (e) {}
|
|
422
|
+
// Print initial progress line (clear line first)
|
|
423
|
+
process.stdout.write('\r\x1b[2KDownloading: 0% — 0.0 MB');
|
|
424
|
+
const progressStart = Date.now();
|
|
425
|
+
function formatEta(sec) {
|
|
426
|
+
if (!isFinite(sec) || sec === null) return '--:--';
|
|
427
|
+
const s = Math.max(0, Math.round(sec));
|
|
428
|
+
const m = Math.floor(s / 60);
|
|
429
|
+
const ss = s % 60;
|
|
430
|
+
return `${m}:${ss.toString().padStart(2, '0')}`;
|
|
431
|
+
}
|
|
432
|
+
await new Promise((resolve, reject) => {
|
|
433
|
+
https.get(tarball, (res) => {
|
|
434
|
+
const fileStream = fs.createWriteStream(tmpFile);
|
|
435
|
+
let downloaded = 0;
|
|
436
|
+
|
|
437
|
+
// Print an updating single-line progress indicator (percent + MB)
|
|
438
|
+
let lastPercent = -1;
|
|
439
|
+
let lastMbReported = -1;
|
|
440
|
+
res.on('data', (chunk) => {
|
|
441
|
+
downloaded += chunk.length;
|
|
442
|
+
const percent = totalBytes ? Math.round((downloaded / totalBytes) * 100) : null;
|
|
443
|
+
const mbDownloaded = +(downloaded / (1024 * 1024));
|
|
444
|
+
const mbTotal = totalBytes ? (totalBytes / (1024 * 1024)) : null;
|
|
445
|
+
|
|
446
|
+
// ETA and speed
|
|
447
|
+
const elapsedSec = Math.max(0.001, (Date.now() - progressStart) / 1000);
|
|
448
|
+
const speed = downloaded / elapsedSec; // bytes/sec
|
|
449
|
+
const remaining = totalBytes ? Math.max(0, totalBytes - downloaded) : null;
|
|
450
|
+
const etaSec = remaining && speed > 0 ? remaining / speed : null;
|
|
451
|
+
|
|
452
|
+
// Build simple ASCII progress bar
|
|
453
|
+
const width = 30;
|
|
454
|
+
const fill = percent !== null ? Math.round((percent / 100) * width) : Math.min(width, Math.max(0, Math.round((mbDownloaded / (mbTotal || 1)) * width)));
|
|
455
|
+
const bar = '█'.repeat(fill) + '-'.repeat(Math.max(0, width - fill));
|
|
456
|
+
|
|
457
|
+
const pctText = percent !== null ? `${percent}%` : '--%';
|
|
458
|
+
const mbText = mbTotal ? `${mbDownloaded.toFixed(1)} MB / ${mbTotal.toFixed(1)} MB` : `${mbDownloaded.toFixed(1)} MB`;
|
|
459
|
+
const etaText = etaSec ? formatEta(etaSec) : '--:--';
|
|
460
|
+
|
|
461
|
+
// Update only when percent changes or every 0.5 MB to reduce noise
|
|
462
|
+
const mbReport = Math.floor(mbDownloaded * 2) / 2;
|
|
463
|
+
if ((percent !== null && percent !== lastPercent) || (percent === null && mbReport !== lastMbReported) || Math.random() < 0.001) {
|
|
464
|
+
lastPercent = percent;
|
|
465
|
+
lastMbReported = mbReport;
|
|
466
|
+
process.stdout.write(`\r\x1b[2K[${bar}] ${pctText} ${mbText} ETA: ${etaText}`);
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
res.on('end', () => fileStream.end());
|
|
471
|
+
res.on('error', (err) => reject(err));
|
|
472
|
+
|
|
473
|
+
fileStream.on('finish', () => resolve());
|
|
474
|
+
fileStream.on('error', (err) => reject(err));
|
|
475
|
+
|
|
476
|
+
res.pipe(fileStream);
|
|
477
|
+
}).on('error', reject);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// Ensure progress line ends and move to next line
|
|
481
|
+
process.stdout.write('\n');
|
|
482
|
+
spinner.start();
|
|
483
|
+
spinner.succeed('Downloaded update');
|
|
484
|
+
|
|
485
|
+
// Install the downloaded tarball
|
|
486
|
+
spinner.start('Installing update...');
|
|
487
|
+
try {
|
|
488
|
+
execSync(`npm install -g "${tmpFile}"`, { stdio: 'inherit', encoding: 'utf8' });
|
|
489
|
+
spinner.succeed('Installed update');
|
|
490
|
+
console.log(chalk.green('\n✅ Successfully updated to v' + updateInfo.latestVersion + '!'));
|
|
491
|
+
console.log(chalk.gray(' Please restart your command to use the new version.\n'));
|
|
492
|
+
// Cleanup
|
|
493
|
+
try { fs.unlinkSync(tmpFile); } catch (e) {}
|
|
494
|
+
process.exit(0);
|
|
495
|
+
} catch (err) {
|
|
496
|
+
spinner.fail('Installation failed');
|
|
497
|
+
console.log(chalk.red('\n❌ Update failed:'), err.message);
|
|
498
|
+
console.log(chalk.yellow(' You can manually update with: ') + chalk.bold.white('npm install -g vibecodingmachine-cli@latest\n'));
|
|
499
|
+
try { fs.unlinkSync(tmpFile); } catch (e) {}
|
|
500
|
+
}
|
|
346
501
|
} catch (error) {
|
|
347
502
|
console.log(chalk.red('\n❌ Update failed:'), error.message);
|
|
348
503
|
console.log(chalk.yellow(' You can manually update with: ') + chalk.bold.white('npm install -g vibecodingmachine-cli@latest\n'));
|
|
@@ -374,16 +529,28 @@ if (!process.argv.slice(2).length) {
|
|
|
374
529
|
const isAuth = await auth.isAuthenticated();
|
|
375
530
|
|
|
376
531
|
if (!isAuth) {
|
|
377
|
-
console.log(chalk.cyan(
|
|
532
|
+
console.log(chalk.cyan(`\n🔐 ${t('cli.auth.opening.browser')}\n`));
|
|
378
533
|
try {
|
|
379
534
|
await auth.login();
|
|
380
|
-
console.log(chalk.green(
|
|
535
|
+
console.log(chalk.green(`\n✓ ${t('cli.auth.success')}\n`));
|
|
381
536
|
} catch (error) {
|
|
382
|
-
console.log(chalk.red(
|
|
537
|
+
console.log(chalk.red(`\n✗ ${t('cli.auth.failed')}:`), error.message);
|
|
383
538
|
process.exit(1);
|
|
384
539
|
}
|
|
385
540
|
}
|
|
386
541
|
|
|
542
|
+
// Check compliance after authentication
|
|
543
|
+
const { checkCompliance } = require('../src/utils/compliance-check');
|
|
544
|
+
const isCompliant = await checkCompliance();
|
|
545
|
+
|
|
546
|
+
if (!isCompliant) {
|
|
547
|
+
console.log(chalk.red(`\n✗ ${t('cli.compliance.failed')}\n`));
|
|
548
|
+
process.exit(1);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Track CLI usage - interactive mode
|
|
552
|
+
await auth.trackCLIActivity('interactive_mode_started');
|
|
553
|
+
|
|
387
554
|
const { startInteractive } = require('../src/utils/interactive');
|
|
388
555
|
await startInteractive();
|
|
389
556
|
})();
|
|
@@ -391,6 +558,47 @@ if (!process.argv.slice(2).length) {
|
|
|
391
558
|
// Check for updates before parsing commands
|
|
392
559
|
(async () => {
|
|
393
560
|
await checkForUpdates();
|
|
561
|
+
|
|
562
|
+
// Check authentication for all commands (except auth commands)
|
|
563
|
+
const command = process.argv[2] || 'unknown';
|
|
564
|
+
const authCommands = ['auth:login', 'auth:logout', 'auth:status'];
|
|
565
|
+
const skipAuthCheck = authCommands.includes(command);
|
|
566
|
+
|
|
567
|
+
const auth = require('../src/utils/auth');
|
|
568
|
+
|
|
569
|
+
if (!skipAuthCheck) {
|
|
570
|
+
const isAuth = await auth.isAuthenticated();
|
|
571
|
+
|
|
572
|
+
if (!isAuth) {
|
|
573
|
+
console.log(chalk.cyan(`\n🔐 ${t('cli.auth.opening.browser')}\n`));
|
|
574
|
+
try {
|
|
575
|
+
await auth.login();
|
|
576
|
+
console.log(chalk.green(`\n✓ ${t('cli.auth.success')}\n`));
|
|
577
|
+
} catch (error) {
|
|
578
|
+
console.log(chalk.red(`\n✗ ${t('cli.auth.failed')}:`), error.message);
|
|
579
|
+
process.exit(1);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Check compliance after authentication (skip for auth commands)
|
|
585
|
+
if (!skipAuthCheck) {
|
|
586
|
+
const { checkCompliance } = require('../src/utils/compliance-check');
|
|
587
|
+
const isCompliant = await checkCompliance();
|
|
588
|
+
|
|
589
|
+
if (!isCompliant) {
|
|
590
|
+
console.log(chalk.red(`\n✗ ${t('cli.compliance.failed')}\n`));
|
|
591
|
+
process.exit(1);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Track CLI usage - command execution
|
|
595
|
+
const args = process.argv.slice(3);
|
|
596
|
+
await auth.trackCLIActivity('command_executed', {
|
|
597
|
+
command,
|
|
598
|
+
args: args.filter(arg => !arg.includes('password') && !arg.includes('token'))
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
394
602
|
// Parse arguments only if commands were provided
|
|
395
603
|
program.parse(process.argv);
|
|
396
604
|
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibecodingmachine-cli",
|
|
3
|
-
"version": "2025.12.
|
|
3
|
+
"version": "2025.12.24-2348",
|
|
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.24-2348",
|
|
29
29
|
"@aws-sdk/client-dynamodb": "^3.600.0",
|
|
30
30
|
"@aws-sdk/lib-dynamodb": "^3.600.0",
|
|
31
31
|
"boxen": "^5.1.2",
|