vibecodingmachine-cli 2025.12.25-25 → 2026.1.3-2209
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 +84 -118
- package/logs/audit/2025-12-27.jsonl +1 -0
- package/logs/audit/2026-01-03.jsonl +2 -0
- package/package.json +2 -2
- package/src/commands/auth.js +5 -1
- package/src/commands/auto-direct.js +219 -213
- package/src/commands/auto.js +6 -3
- package/src/commands/computers.js +9 -0
- package/src/commands/feature.js +123 -0
- package/src/commands/repo.js +27 -22
- package/src/commands/requirements-remote.js +24 -1
- package/src/commands/requirements.js +129 -9
- package/src/commands/setup.js +2 -1
- package/src/commands/sync.js +7 -1
- package/src/utils/auth.js +20 -13
- package/src/utils/config.js +14 -1
- package/src/utils/first-run.js +8 -6
- package/src/utils/interactive.js +613 -53
- package/src/utils/prompt-helper.js +64 -0
- package/tests/home-bootstrap.test.js +76 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const inquirer = require('inquirer');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Enhanced inquirer.prompt that shows a helpful hint for questions with defaults
|
|
6
|
+
* @param {Array} questions - Array of inquirer question objects
|
|
7
|
+
* @param {Object} options - Options object
|
|
8
|
+
* @param {boolean} options.showDefaultHint - Whether to show the default hint (default: true)
|
|
9
|
+
* @returns {Promise} - Promise resolving to answers
|
|
10
|
+
*/
|
|
11
|
+
async function promptWithDefaults(questions, options = {}) {
|
|
12
|
+
const { showDefaultHint = true } = options;
|
|
13
|
+
|
|
14
|
+
// Check if any questions have defaults and if we should show the hint
|
|
15
|
+
const hasDefaults = questions.some(q => q.default !== undefined);
|
|
16
|
+
|
|
17
|
+
if (showDefaultHint && hasDefaults) {
|
|
18
|
+
// Show the hint in gray before the first question with a default
|
|
19
|
+
console.log(chalk.gray('(Capital letters are defaults--press return to select the defaults)'));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return await inquirer.prompt(questions);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Track whether we've shown the default hint in this session
|
|
27
|
+
* This ensures we only show it once per CLI session
|
|
28
|
+
*/
|
|
29
|
+
let hasShownDefaultHint = false;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Enhanced inquirer.prompt that shows the default hint only once per session
|
|
33
|
+
* @param {Array} questions - Array of inquirer question objects
|
|
34
|
+
* @param {Object} options - Options object
|
|
35
|
+
* @param {boolean} options.forceShowHint - Force showing the hint even if already shown
|
|
36
|
+
* @returns {Promise} - Promise resolving to answers
|
|
37
|
+
*/
|
|
38
|
+
async function promptWithDefaultsOnce(questions, options = {}) {
|
|
39
|
+
const { forceShowHint = false } = options;
|
|
40
|
+
|
|
41
|
+
// Check if any questions have defaults
|
|
42
|
+
const hasDefaults = questions.some(q => q.default !== undefined);
|
|
43
|
+
|
|
44
|
+
if (hasDefaults && (!hasShownDefaultHint || forceShowHint)) {
|
|
45
|
+
// Show the hint in gray before the first question with a default
|
|
46
|
+
console.log(chalk.gray('(Capital letters are defaults--press return to select the defaults)'));
|
|
47
|
+
hasShownDefaultHint = true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return await inquirer.prompt(questions);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Reset the hint tracking (useful for testing or new sessions)
|
|
55
|
+
*/
|
|
56
|
+
function resetDefaultHint() {
|
|
57
|
+
hasShownDefaultHint = false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = {
|
|
61
|
+
promptWithDefaults,
|
|
62
|
+
promptWithDefaultsOnce,
|
|
63
|
+
resetDefaultHint
|
|
64
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const os = require('os');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
|
|
5
|
+
describe('interactive home directory bootstrap', () => {
|
|
6
|
+
const tmpHome = path.join(os.tmpdir(), `vibecodingmachine_test_home_${Date.now()}`);
|
|
7
|
+
const tmpConfig = path.join(os.tmpdir(), `vibecodingmachine_test_config_${Date.now()}.json`);
|
|
8
|
+
|
|
9
|
+
beforeAll(async () => {
|
|
10
|
+
process.env.VIBECODINGMACHINE_CONFIG_PATH = tmpConfig;
|
|
11
|
+
await fs.ensureDir(tmpHome);
|
|
12
|
+
|
|
13
|
+
jest.resetModules();
|
|
14
|
+
|
|
15
|
+
jest.spyOn(os, 'homedir').mockReturnValue(tmpHome);
|
|
16
|
+
|
|
17
|
+
process.chdir(tmpHome);
|
|
18
|
+
|
|
19
|
+
const prompts = [];
|
|
20
|
+
jest.doMock('inquirer', () => ({
|
|
21
|
+
prompt: async (questions) => {
|
|
22
|
+
prompts.push(questions);
|
|
23
|
+
const q0 = Array.isArray(questions) ? questions[0] : questions;
|
|
24
|
+
|
|
25
|
+
if (q0 && q0.name === 'shouldCreateCodeDir') {
|
|
26
|
+
return { shouldCreateCodeDir: true };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (q0 && q0.name === 'projectName') {
|
|
30
|
+
return { projectName: 'My Project Name' };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
jest.doMock('vibecodingmachine-core', () => ({
|
|
38
|
+
checkVibeCodingMachineExists: async () => ({ insideExists: false, siblingExists: false }),
|
|
39
|
+
getHostname: () => 'test-host',
|
|
40
|
+
getRequirementsFilename: async () => 'REQUIREMENTS.md',
|
|
41
|
+
requirementsExists: async () => false,
|
|
42
|
+
isComputerNameEnabled: async () => false,
|
|
43
|
+
t: (k) => k,
|
|
44
|
+
detectLocale: () => 'en',
|
|
45
|
+
setLocale: () => {}
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
afterAll(async () => {
|
|
52
|
+
jest.restoreAllMocks();
|
|
53
|
+
delete process.env.VIBECODINGMACHINE_CONFIG_PATH;
|
|
54
|
+
await fs.remove(tmpConfig).catch(() => {});
|
|
55
|
+
await fs.remove(tmpHome).catch(() => {});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('bootstraps ~/code/<project> from home and can init .vibecodingmachine inside it', async () => {
|
|
59
|
+
const { bootstrapProjectIfInHomeDir } = require('../src/utils/interactive');
|
|
60
|
+
await bootstrapProjectIfInHomeDir();
|
|
61
|
+
|
|
62
|
+
const projectDir = path.join(tmpHome, 'code', 'my-project-name');
|
|
63
|
+
const projectDirReal = await fs.realpath(projectDir);
|
|
64
|
+
expect(await fs.realpath(process.cwd())).toBe(projectDirReal);
|
|
65
|
+
expect(await fs.pathExists(projectDirReal)).toBe(true);
|
|
66
|
+
|
|
67
|
+
const { initRepo } = require('../src/commands/repo');
|
|
68
|
+
await initRepo({ location: 'inside' });
|
|
69
|
+
|
|
70
|
+
const vcmDir = path.join(projectDirReal, '.vibecodingmachine');
|
|
71
|
+
expect(await fs.pathExists(vcmDir)).toBe(true);
|
|
72
|
+
|
|
73
|
+
const requirementsPath = path.join(vcmDir, 'REQUIREMENTS.md');
|
|
74
|
+
expect(await fs.pathExists(requirementsPath)).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
});
|