vibecodingmachine-cli 2025.12.6-1702 → 2025.12.24-2348
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/vibecodingmachine.js +260 -52
- package/logs/audit/2025-12-24.jsonl +2 -0
- package/package.json +2 -2
- package/reproduce_issue.js +160 -0
- package/src/commands/auth.js +19 -19
- package/src/commands/auto-direct.js +133 -93
- package/src/commands/auto.js +165 -67
- package/src/commands/computers.js +62 -59
- package/src/commands/locale.js +73 -0
- package/src/commands/repo.js +0 -1
- package/src/commands/requirements-remote.js +14 -9
- package/src/commands/requirements.js +29 -3
- package/src/commands/setup.js +14 -13
- package/src/commands/status.js +17 -18
- package/src/commands/sync.js +72 -71
- package/src/utils/agent-selector.js +50 -0
- package/src/utils/antigravity-installer.js +212 -0
- package/src/utils/antigravity-js-handler.js +60 -0
- package/src/utils/asset-cleanup.js +0 -1
- package/src/utils/auth.js +149 -2
- package/src/utils/auto-mode-ansi-ui.js +0 -1
- package/src/utils/auto-mode-simple-ui.js +1 -1
- package/src/utils/compliance-check.js +166 -0
- package/src/utils/config.js +27 -1
- package/src/utils/copy-with-progress.js +167 -0
- package/src/utils/download-with-progress.js +84 -0
- package/src/utils/first-run.js +189 -71
- package/src/utils/interactive.js +419 -362
- package/src/utils/kiro-installer.js +56 -24
- package/src/utils/persistent-header.js +1 -3
- package/src/utils/provider-registry.js +43 -5
- package/src/utils/user-tracking.js +300 -0
- package/tests/requirements-navigator-buildtree-await.test.js +28 -0
package/src/utils/first-run.js
CHANGED
|
@@ -5,7 +5,91 @@ const fs = require('fs-extra');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const os = require('os');
|
|
7
7
|
const { getProviderDefinitions, saveProviderPreferences, getDefaultProviderOrder } = require('./provider-registry');
|
|
8
|
-
const { isKiroInstalled
|
|
8
|
+
const { isKiroInstalled } = require('./kiro-installer');
|
|
9
|
+
const { t } = require('vibecodingmachine-core');
|
|
10
|
+
|
|
11
|
+
const { execSync } = require('child_process');
|
|
12
|
+
|
|
13
|
+
async function checkAppOrBinary(names = [], binaries = []) {
|
|
14
|
+
// names: app bundle base names (e.g., 'Cursor' -> /Applications/Cursor.app)
|
|
15
|
+
// binaries: CLI binary names to check on PATH (e.g., 'code')
|
|
16
|
+
const platform = os.platform();
|
|
17
|
+
// Check common application directories
|
|
18
|
+
if (platform === 'darwin') {
|
|
19
|
+
const appDirs = ['/Applications', path.join(os.homedir(), 'Applications')];
|
|
20
|
+
for (const appName of names) {
|
|
21
|
+
for (const dir of appDirs) {
|
|
22
|
+
try {
|
|
23
|
+
const p = path.join(dir, `${appName}.app`);
|
|
24
|
+
if (await fs.pathExists(p)) {
|
|
25
|
+
// Ensure this is a real application bundle (has Contents/MacOS executable)
|
|
26
|
+
try {
|
|
27
|
+
const macosDir = path.join(p, 'Contents', 'MacOS');
|
|
28
|
+
const exists = await fs.pathExists(macosDir);
|
|
29
|
+
if (exists) {
|
|
30
|
+
const files = await fs.readdir(macosDir);
|
|
31
|
+
if (files && files.length > 0) {
|
|
32
|
+
// Prefer to ensure the app is usable: use spctl to assess, fallback to quarantine xattr
|
|
33
|
+
try {
|
|
34
|
+
// spctl returns non-zero on rejected/invalid apps
|
|
35
|
+
execSync(`spctl --assess -v "${p}"`, { stdio: 'ignore', timeout: 5000 });
|
|
36
|
+
// additionally validate codesign quickly (timeout to avoid hangs)
|
|
37
|
+
try {
|
|
38
|
+
execSync(`codesign -v --deep --strict "${p}"`, { stdio: 'ignore', timeout: 5000 });
|
|
39
|
+
return true;
|
|
40
|
+
} catch (csErr) {
|
|
41
|
+
// codesign failed or timed out — treat as not usable/damaged
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
} catch (e) {
|
|
45
|
+
// spctl failed or timed out — check if app has quarantine attribute
|
|
46
|
+
try {
|
|
47
|
+
const out = execSync(`xattr -p com.apple.quarantine "${p}" 2>/dev/null || true`, { encoding: 'utf8' }).trim();
|
|
48
|
+
if (!out) {
|
|
49
|
+
// no quarantine attribute but spctl failed — be conservative and treat as not installed
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
// If quarantine attribute exists, treat as not installed (damaged/not allowed)
|
|
53
|
+
return false;
|
|
54
|
+
} catch (e2) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} catch (e) {
|
|
61
|
+
// if we can't stat inside, be conservative and continue searching
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} catch (e) {
|
|
65
|
+
/* ignore */
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Check PATH for known binaries
|
|
72
|
+
for (const bin of binaries) {
|
|
73
|
+
try {
|
|
74
|
+
execSync(`which ${bin}`, { stdio: 'ignore' });
|
|
75
|
+
return true;
|
|
76
|
+
} catch (e) {
|
|
77
|
+
/* not found */
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Check common Homebrew bin locations
|
|
82
|
+
const brewPaths = ['/opt/homebrew/bin', '/usr/local/bin'];
|
|
83
|
+
for (const bin of binaries) {
|
|
84
|
+
for (const brew of brewPaths) {
|
|
85
|
+
try {
|
|
86
|
+
if (await fs.pathExists(path.join(brew, bin))) return true;
|
|
87
|
+
} catch (e) { /* ignore */ }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
9
93
|
|
|
10
94
|
async function checkFirstRun() {
|
|
11
95
|
const configDir = path.join(os.homedir(), '.vibecodingmachine');
|
|
@@ -68,7 +152,7 @@ async function checkFirstRun() {
|
|
|
68
152
|
await inquirer.prompt([{
|
|
69
153
|
type: 'input',
|
|
70
154
|
name: 'continue',
|
|
71
|
-
message: '
|
|
155
|
+
message: t('interactive.press.enter.continue')
|
|
72
156
|
}]);
|
|
73
157
|
}
|
|
74
158
|
|
|
@@ -85,7 +169,6 @@ async function checkFirstRun() {
|
|
|
85
169
|
// Simple simulation of detection for standard apps on macOS
|
|
86
170
|
// In a real scenario, we might check other paths or registry on Windows
|
|
87
171
|
const platform = os.platform();
|
|
88
|
-
const getAppPath = (name) => platform === 'darwin' ? `/Applications/${name}.app` : null;
|
|
89
172
|
|
|
90
173
|
for (const ide of ideDefinitions) {
|
|
91
174
|
let installed = false;
|
|
@@ -93,15 +176,17 @@ async function checkFirstRun() {
|
|
|
93
176
|
if (ide.id === 'kiro') {
|
|
94
177
|
installed = isKiroInstalled();
|
|
95
178
|
} else if (ide.id === 'cursor') {
|
|
96
|
-
|
|
179
|
+
// Cursor: check app bundle and 'cursor' binary
|
|
180
|
+
installed = await checkAppOrBinary(['Cursor'], ['cursor']);
|
|
97
181
|
} else if (ide.id === 'windsurf') {
|
|
98
|
-
|
|
182
|
+
// Windsurf: check app bundle and common binary
|
|
183
|
+
installed = await checkAppOrBinary(['Windsurf'], ['windsurf']);
|
|
99
184
|
} else if (ide.id === 'vscode') {
|
|
100
|
-
|
|
185
|
+
// VS Code: check app bundle and 'code' CLI
|
|
186
|
+
installed = await checkAppOrBinary(['Visual Studio Code', 'Visual Studio Code - Insiders'], ['code', 'code-insiders']);
|
|
101
187
|
} else if (ide.id === 'antigravity') {
|
|
102
|
-
// Antigravity
|
|
103
|
-
|
|
104
|
-
installed = true; // Assume available
|
|
188
|
+
// Antigravity: check app bundle and 'antigravity' binary
|
|
189
|
+
installed = await checkAppOrBinary(['Antigravity'], ['antigravity']);
|
|
105
190
|
}
|
|
106
191
|
|
|
107
192
|
if (installed) {
|
|
@@ -140,84 +225,117 @@ async function checkFirstRun() {
|
|
|
140
225
|
selectedIDEs = response.selectedIDEs;
|
|
141
226
|
}
|
|
142
227
|
|
|
143
|
-
// 4. Handle Installations
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
228
|
+
// 4. Handle Installations (Generic)
|
|
229
|
+
// For any selected IDE that wasn't detected, try to invoke a matching installer module
|
|
230
|
+
// Installer modules are expected to live next to this file as `<id>-installer.js` and
|
|
231
|
+
// export an installation function like `install<IdPascal>()` or `install`.
|
|
232
|
+
for (const ideId of selectedIDEs) {
|
|
233
|
+
// determine if already installed using existing checks
|
|
234
|
+
let alreadyInstalled = false;
|
|
235
|
+
try {
|
|
236
|
+
if (ideId === 'kiro') {
|
|
237
|
+
alreadyInstalled = isKiroInstalled() || await checkAppOrBinary(['AWS Kiro', 'Kiro'], ['kiro']);
|
|
238
|
+
} else if (ideId === 'cursor') {
|
|
239
|
+
alreadyInstalled = await checkAppOrBinary(['Cursor'], ['cursor']);
|
|
240
|
+
} else if (ideId === 'windsurf') {
|
|
241
|
+
alreadyInstalled = await checkAppOrBinary(['Windsurf'], ['windsurf']);
|
|
242
|
+
} else if (ideId === 'vscode') {
|
|
243
|
+
alreadyInstalled = await checkAppOrBinary(['Visual Studio Code', 'Visual Studio Code - Insiders'], ['code', 'code-insiders']);
|
|
244
|
+
} else if (ideId === 'antigravity') {
|
|
245
|
+
alreadyInstalled = await checkAppOrBinary(['Antigravity'], ['antigravity']);
|
|
153
246
|
} else {
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
chalk.cyan('You must sign in for the CLI to control the IDE.'),
|
|
162
|
-
{
|
|
163
|
-
padding: 1,
|
|
164
|
-
margin: 1,
|
|
165
|
-
borderStyle: 'round',
|
|
166
|
-
borderColor: 'yellow'
|
|
167
|
-
}
|
|
168
|
-
));
|
|
247
|
+
// default fallback: try binary name same as id
|
|
248
|
+
alreadyInstalled = await checkAppOrBinary([], [ideId]);
|
|
249
|
+
}
|
|
250
|
+
} catch (e) {
|
|
251
|
+
// detection error: assume not installed
|
|
252
|
+
alreadyInstalled = false;
|
|
253
|
+
}
|
|
169
254
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
255
|
+
if (alreadyInstalled) continue;
|
|
256
|
+
|
|
257
|
+
// Attempt to locate and run an installer module for this IDE
|
|
258
|
+
let installerModule = null;
|
|
259
|
+
const tryPaths = [`./${ideId}-installer`, `../utils/${ideId}-installer`];
|
|
260
|
+
for (const p of tryPaths) {
|
|
261
|
+
try {
|
|
262
|
+
// require may throw if module doesn't exist
|
|
263
|
+
installerModule = require(p);
|
|
264
|
+
break;
|
|
265
|
+
} catch (e) {
|
|
266
|
+
// ignore and try next
|
|
267
|
+
}
|
|
268
|
+
}
|
|
175
269
|
|
|
176
|
-
|
|
177
|
-
|
|
270
|
+
if (!installerModule) {
|
|
271
|
+
console.log(chalk.gray(`No installer module found for ${ideId}, skipping automated install.`));
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
178
274
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
275
|
+
// Resolve a callable install function from the module
|
|
276
|
+
const pascal = ideId.split(/[-_]/).map(s => s[0].toUpperCase() + s.slice(1)).join('');
|
|
277
|
+
const candidateNames = [`install${pascal}`, 'install', pascal, 'default'];
|
|
278
|
+
let installerFn = null;
|
|
279
|
+
for (const name of candidateNames) {
|
|
280
|
+
if (name === 'default' && typeof installerModule === 'function') {
|
|
281
|
+
installerFn = installerModule;
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
if (installerModule && typeof installerModule[name] === 'function') {
|
|
285
|
+
installerFn = installerModule[name];
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (!installerFn) {
|
|
291
|
+
console.log(chalk.gray(`Installer found for ${ideId} but no callable export (tried ${candidateNames.join(', ')}).`));
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
194
294
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
295
|
+
console.log(chalk.cyan(`\n🔧 Installing ${ideId}...`));
|
|
296
|
+
try {
|
|
297
|
+
const ok = await installerFn();
|
|
298
|
+
if (!ok) {
|
|
299
|
+
console.log(chalk.yellow(`\n⚠️ ${ideId} installation failed or was skipped.`));
|
|
300
|
+
} else {
|
|
301
|
+
console.log(chalk.green(`\n✅ ${ideId} installation complete.`));
|
|
200
302
|
}
|
|
303
|
+
} catch (err) {
|
|
304
|
+
console.log(chalk.red(`${ideId} installation error:`), err.message || err);
|
|
201
305
|
}
|
|
202
306
|
}
|
|
203
307
|
|
|
204
308
|
// 5. Configure Preferences
|
|
205
|
-
//
|
|
309
|
+
// Re-detect IDEs to ensure we only enable actually installed ones
|
|
310
|
+
const reDetected = [];
|
|
311
|
+
for (const ideDef of ideDefinitions) {
|
|
312
|
+
let installedNow = false;
|
|
313
|
+
if (ideDef.id === 'kiro') {
|
|
314
|
+
installedNow = isKiroInstalled() || await checkAppOrBinary(['AWS Kiro', 'Kiro'], ['kiro']);
|
|
315
|
+
} else if (ideDef.id === 'cursor') {
|
|
316
|
+
installedNow = await checkAppOrBinary(['Cursor'], ['cursor']);
|
|
317
|
+
} else if (ideDef.id === 'windsurf') {
|
|
318
|
+
installedNow = await checkAppOrBinary(['Windsurf'], ['windsurf']);
|
|
319
|
+
} else if (ideDef.id === 'vscode') {
|
|
320
|
+
installedNow = await checkAppOrBinary(['Visual Studio Code', 'Visual Studio Code - Insiders'], ['code', 'code-insiders']);
|
|
321
|
+
} else if (ideDef.id === 'antigravity') {
|
|
322
|
+
installedNow = await checkAppOrBinary(['Antigravity'], ['antigravity']);
|
|
323
|
+
}
|
|
324
|
+
if (installedNow) reDetected.push(ideDef.id);
|
|
325
|
+
}
|
|
326
|
+
|
|
206
327
|
const defaultOrder = getDefaultProviderOrder();
|
|
207
328
|
const enabledMap = {};
|
|
208
|
-
|
|
209
|
-
defaultOrder.forEach(id => {
|
|
210
|
-
// Enable if it was detected OR selected
|
|
211
|
-
// For LLMs (not 'ide' type), keep enabled by default
|
|
329
|
+
for (const id of defaultOrder) {
|
|
212
330
|
const def = definitions.find(d => d.id === id);
|
|
213
331
|
if (def && def.type === 'ide') {
|
|
214
|
-
const
|
|
332
|
+
const isDetectedNow = reDetected.includes(id);
|
|
215
333
|
const isSelected = selectedIDEs.includes(id);
|
|
216
|
-
enabledMap[id] =
|
|
334
|
+
enabledMap[id] = isDetectedNow || isSelected;
|
|
217
335
|
} else {
|
|
218
336
|
enabledMap[id] = true; // Keep LLMs enabled by default
|
|
219
337
|
}
|
|
220
|
-
}
|
|
338
|
+
}
|
|
221
339
|
|
|
222
340
|
// Save initial preferences. provider-registry.js usually saves to ~/.config/vibecodingmachine/config.json
|
|
223
341
|
// But here we are checking ~/.vibecodingmachine.
|
|
@@ -261,7 +379,7 @@ async function checkFirstRun() {
|
|
|
261
379
|
await inquirer.prompt([{
|
|
262
380
|
type: 'input',
|
|
263
381
|
name: 'continue',
|
|
264
|
-
message: '
|
|
382
|
+
message: t('interactive.press.enter.continue')
|
|
265
383
|
}]);
|
|
266
384
|
}
|
|
267
385
|
|
|
@@ -278,7 +396,7 @@ async function checkFirstRun() {
|
|
|
278
396
|
}
|
|
279
397
|
));
|
|
280
398
|
|
|
281
|
-
console.log(chalk.cyan('
|
|
399
|
+
console.log(chalk.cyan(t('interactive.press.any.key.menu')));
|
|
282
400
|
|
|
283
401
|
await new Promise((resolve) => {
|
|
284
402
|
process.stdin.setRawMode(true);
|