vibecodingmachine-cli 2026.1.23-1010 → 2026.1.29-1432
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/package.json +14 -5
- package/src/utils/interactive.js +53 -3
- package/.allnightai/REQUIREMENTS.md +0 -11
- package/.eslintrc.js +0 -16
- package/__tests__/antigravity-js-handler.test.js +0 -23
- package/__tests__/provider-manager.test.js +0 -84
- package/__tests__/provider-rate-cache.test.js +0 -27
- package/jest.config.js +0 -8
- package/logs/audit/2025-11-07.jsonl +0 -2
- package/logs/audit/2025-12-24.jsonl +0 -2
- package/logs/audit/2025-12-27.jsonl +0 -1
- package/logs/audit/2026-01-03.jsonl +0 -2
- package/repro_open.js +0 -13
- package/reproduce_issue.js +0 -160
- package/reset_provider_order.js +0 -21
- package/scripts/README.md +0 -128
- package/scripts/auto-start-wrapper.sh +0 -92
- package/scripts/convert-requirements.js +0 -35
- package/scripts/debug-parse.js +0 -24
- package/src/commands/auto.js.bak +0 -710
- package/src/utils/auto-mode-ui.js.bak.blessed +0 -207
- package/tests/antigravity-js-handler.test.js +0 -23
- package/tests/auto-mode.test.js +0 -37
- package/tests/config.test.js +0 -34
- package/tests/home-bootstrap.test.js +0 -76
- package/tests/integration/health-tracking.integration.test.js +0 -284
- package/tests/provider-manager.test.js +0 -92
- package/tests/rate-limit-display.test.js +0 -44
- package/tests/requirements-bullet-parsing.test.js +0 -15
- package/tests/requirements-converter.test.js +0 -42
- package/tests/requirements-heading-count.test.js +0 -27
- package/tests/requirements-legacy-parsing.test.js +0 -15
- package/tests/requirements-navigator-buildtree-await.test.js +0 -28
- package/tests/requirements-parse-integration.test.js +0 -44
- package/tests/wait-for-ide-completion.test.js +0 -56
- package/tests/wait-for-ide-quota-detection-cursor-screenshot.test.js +0 -61
- package/tests/wait-for-ide-quota-detection-cursor.test.js +0 -60
- package/tests/wait-for-ide-quota-detection-negative.test.js +0 -45
- package/tests/wait-for-ide-quota-detection.test.js +0 -59
- package/verify_fix.js +0 -36
- package/verify_ui.js +0 -38
package/package.json
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibecodingmachine-cli",
|
|
3
|
-
"version": "2026.01.
|
|
3
|
+
"version": "2026.01.29-1432",
|
|
4
4
|
"description": "Command-line interface for Vibe Coding Machine - Autonomous development",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"vibecodingmachine": "./bin/vibecodingmachine.js"
|
|
8
|
-
"vcm": "./bin/vibecodingmachine.js"
|
|
7
|
+
"vibecodingmachine": "./bin/vibecodingmachine.js"
|
|
9
8
|
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/**/*.js",
|
|
11
|
+
"src/**/*.js",
|
|
12
|
+
"src/**/*.json",
|
|
13
|
+
"scripts/postinstall.js",
|
|
14
|
+
"!src/**/*.test.js",
|
|
15
|
+
"!src/**/__tests__/**",
|
|
16
|
+
"!src/**/tests/**"
|
|
17
|
+
],
|
|
10
18
|
"scripts": {
|
|
11
19
|
"postinstall": "node scripts/postinstall.js",
|
|
12
20
|
"test": "jest",
|
|
@@ -25,7 +33,6 @@
|
|
|
25
33
|
"author": "Vibe Coding Machine Team",
|
|
26
34
|
"license": "MIT",
|
|
27
35
|
"dependencies": {
|
|
28
|
-
"vibecodingmachine-core": "^2026.01.23-1010",
|
|
29
36
|
"@aws-sdk/client-dynamodb": "^3.600.0",
|
|
30
37
|
"@aws-sdk/lib-dynamodb": "^3.600.0",
|
|
31
38
|
"boxen": "^5.1.2",
|
|
@@ -43,7 +50,9 @@
|
|
|
43
50
|
"open": "^11.0.0",
|
|
44
51
|
"ora": "^5.4.1",
|
|
45
52
|
"react": "^19.2.0",
|
|
46
|
-
"
|
|
53
|
+
"screenshot-desktop": "^1.15.3",
|
|
54
|
+
"table": "^6.8.1",
|
|
55
|
+
"vibecodingmachine-core": "^2026.01.29-1432"
|
|
47
56
|
},
|
|
48
57
|
"devDependencies": {
|
|
49
58
|
"eslint": "^8.57.0",
|
package/src/utils/interactive.js
CHANGED
|
@@ -1138,6 +1138,49 @@ async function handleFeedbackSubmission() {
|
|
|
1138
1138
|
const userProfile = await getUserProfile();
|
|
1139
1139
|
const userEmail = userProfile ? userProfile.email : 'anonymous';
|
|
1140
1140
|
|
|
1141
|
+
// Ask if user wants to include a screenshot
|
|
1142
|
+
let includeScreenshot = false;
|
|
1143
|
+
let screenshotData = null;
|
|
1144
|
+
|
|
1145
|
+
try {
|
|
1146
|
+
const { screenshot } = await inquirer.prompt([{
|
|
1147
|
+
type: 'confirm',
|
|
1148
|
+
name: 'screenshot',
|
|
1149
|
+
message: 'Include a screenshot with your feedback?',
|
|
1150
|
+
default: false
|
|
1151
|
+
}]);
|
|
1152
|
+
|
|
1153
|
+
includeScreenshot = screenshot;
|
|
1154
|
+
|
|
1155
|
+
if (includeScreenshot) {
|
|
1156
|
+
console.log(chalk.gray('📸 Capturing screenshot...'));
|
|
1157
|
+
try {
|
|
1158
|
+
const screenshot = require('screenshot-desktop');
|
|
1159
|
+
const imgBuffer = await screenshot({ format: 'png' });
|
|
1160
|
+
|
|
1161
|
+
// Convert to base64 and check size
|
|
1162
|
+
const base64 = imgBuffer.toString('base64');
|
|
1163
|
+
const dataUrl = `data:image/png;base64,${base64}`;
|
|
1164
|
+
const size = Buffer.byteLength(dataUrl, 'utf8');
|
|
1165
|
+
|
|
1166
|
+
// Much stricter size limit - 100KB max
|
|
1167
|
+
if (size > 100 * 1024) {
|
|
1168
|
+
console.log(chalk.yellow('⚠️ Screenshot is too large, submitting feedback without screenshot'));
|
|
1169
|
+
includeScreenshot = false;
|
|
1170
|
+
} else {
|
|
1171
|
+
screenshotData = dataUrl;
|
|
1172
|
+
console.log(chalk.green(`✅ Screenshot captured (${(size / 1024).toFixed(2)} KB)`));
|
|
1173
|
+
}
|
|
1174
|
+
} catch (screenshotError) {
|
|
1175
|
+
console.log(chalk.yellow('⚠️ Failed to capture screenshot, continuing without it'));
|
|
1176
|
+
includeScreenshot = false;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
} catch (err) {
|
|
1180
|
+
// User cancelled or error occurred
|
|
1181
|
+
includeScreenshot = false;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1141
1184
|
console.log(chalk.gray('\n' + t('interactive.feedback.comment.instructions') + '\n'));
|
|
1142
1185
|
|
|
1143
1186
|
const commentLines = [];
|
|
@@ -1186,12 +1229,19 @@ async function handleFeedbackSubmission() {
|
|
|
1186
1229
|
userDb.setAuthToken(token);
|
|
1187
1230
|
}
|
|
1188
1231
|
|
|
1189
|
-
|
|
1232
|
+
const feedbackData = {
|
|
1190
1233
|
email: userEmail,
|
|
1191
1234
|
comment: comment,
|
|
1192
1235
|
interface: 'cli',
|
|
1193
|
-
version: pkg.version
|
|
1194
|
-
|
|
1236
|
+
version: pkg.version,
|
|
1237
|
+
type: 'cli_feedback'
|
|
1238
|
+
};
|
|
1239
|
+
|
|
1240
|
+
if (includeScreenshot && screenshotData) {
|
|
1241
|
+
feedbackData.screenshot = screenshotData;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
await userDb.submitFeedback(feedbackData);
|
|
1195
1245
|
|
|
1196
1246
|
console.log(chalk.green('\n✓ ' + t('interactive.feedback.success')));
|
|
1197
1247
|
} catch (error) {
|
package/.eslintrc.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
env: {
|
|
3
|
-
node: true,
|
|
4
|
-
es2021: true,
|
|
5
|
-
jest: true
|
|
6
|
-
},
|
|
7
|
-
extends: 'eslint:recommended',
|
|
8
|
-
parserOptions: {
|
|
9
|
-
ecmaVersion: 2021,
|
|
10
|
-
sourceType: 'module'
|
|
11
|
-
},
|
|
12
|
-
rules: {
|
|
13
|
-
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
|
14
|
-
'no-console': 'off'
|
|
15
|
-
}
|
|
16
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
const { handleAntigravityRateLimit } = require('../src/utils/antigravity-js-handler');
|
|
2
|
-
const providerRegistry = require('../src/utils/provider-registry');
|
|
3
|
-
|
|
4
|
-
jest.mock('../src/utils/provider-registry');
|
|
5
|
-
|
|
6
|
-
describe('handleAntigravityRateLimit', () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
jest.resetAllMocks();
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
test('suggests next provider and does not persistently disable antigravity', async () => {
|
|
12
|
-
providerRegistry.getProviderPreferences.mockResolvedValue({
|
|
13
|
-
order: ['antigravity', 'vscode'],
|
|
14
|
-
enabled: { antigravity: true, vscode: true }
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
const result = await handleAntigravityRateLimit();
|
|
18
|
-
|
|
19
|
-
expect(result.success).toBe(true);
|
|
20
|
-
expect(result.nextProvider).toBe('vscode');
|
|
21
|
-
expect(providerRegistry.saveProviderPreferences).not.toHaveBeenCalled();
|
|
22
|
-
});
|
|
23
|
-
});
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
const providerRegistry = require('../src/utils/provider-registry');
|
|
2
|
-
const interactive = require('../src/utils/interactive');
|
|
3
|
-
|
|
4
|
-
jest.mock('../src/utils/provider-registry');
|
|
5
|
-
|
|
6
|
-
describe('showProviderManagerMenu', () => {
|
|
7
|
-
const origIsTTY = process.stdin.isTTY;
|
|
8
|
-
const origSetRawMode = process.stdin.setRawMode;
|
|
9
|
-
|
|
10
|
-
beforeAll(() => {
|
|
11
|
-
// Ensure stdin behaves like a TTY for the menu
|
|
12
|
-
process.stdin.isTTY = true;
|
|
13
|
-
process.stdin.setRawMode = () => {};
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
afterAll(() => {
|
|
17
|
-
process.stdin.isTTY = origIsTTY;
|
|
18
|
-
process.stdin.setRawMode = origSetRawMode;
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
jest.resetAllMocks();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test('pressing left after reordering calls saveProviderPreferences', async () => {
|
|
26
|
-
providerRegistry.getProviderDefinitions.mockReturnValue([
|
|
27
|
-
{ id: 'groq', name: 'Groq' },
|
|
28
|
-
{ id: 'antigravity', name: 'Antigravity' }
|
|
29
|
-
]);
|
|
30
|
-
|
|
31
|
-
providerRegistry.getProviderPreferences.mockResolvedValue({
|
|
32
|
-
order: ['groq', 'antigravity'],
|
|
33
|
-
enabled: { groq: true, antigravity: true }
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
providerRegistry.saveProviderPreferences.mockResolvedValue();
|
|
37
|
-
|
|
38
|
-
// Start the menu
|
|
39
|
-
const menuPromise = interactive.showProviderManagerMenu();
|
|
40
|
-
|
|
41
|
-
// Allow the menu to initialize
|
|
42
|
-
await new Promise(resolve => setImmediate(resolve));
|
|
43
|
-
|
|
44
|
-
// Simulate 'j' (reorder downward)
|
|
45
|
-
process.stdin.emit('keypress', 'j', { name: 'j' });
|
|
46
|
-
await new Promise(resolve => setImmediate(resolve));
|
|
47
|
-
|
|
48
|
-
// Simulate left arrow to save and exit
|
|
49
|
-
process.stdin.emit('keypress', undefined, { name: 'left' });
|
|
50
|
-
|
|
51
|
-
await menuPromise; // wait for menu to finish
|
|
52
|
-
|
|
53
|
-
expect(providerRegistry.saveProviderPreferences).toHaveBeenCalledTimes(1);
|
|
54
|
-
expect(providerRegistry.saveProviderPreferences).toHaveBeenCalledWith(['antigravity', 'groq'], { groq: true, antigravity: true });
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test('pressing escape after reordering does NOT call saveProviderPreferences', async () => {
|
|
58
|
-
providerRegistry.getProviderDefinitions.mockReturnValue([
|
|
59
|
-
{ id: 'groq', name: 'Groq' },
|
|
60
|
-
{ id: 'antigravity', name: 'Antigravity' }
|
|
61
|
-
]);
|
|
62
|
-
|
|
63
|
-
providerRegistry.getProviderPreferences.mockResolvedValue({
|
|
64
|
-
order: ['groq', 'antigravity'],
|
|
65
|
-
enabled: { groq: true, antigravity: true }
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
providerRegistry.saveProviderPreferences.mockResolvedValue();
|
|
69
|
-
|
|
70
|
-
const menuPromise = interactive.showProviderManagerMenu();
|
|
71
|
-
await new Promise(resolve => setImmediate(resolve));
|
|
72
|
-
|
|
73
|
-
// Make a change
|
|
74
|
-
process.stdin.emit('keypress', 'j', { name: 'j' });
|
|
75
|
-
await new Promise(resolve => setImmediate(resolve));
|
|
76
|
-
|
|
77
|
-
// Press escape to cancel (should not persist)
|
|
78
|
-
process.stdin.emit('keypress', undefined, { name: 'escape' });
|
|
79
|
-
|
|
80
|
-
await menuPromise;
|
|
81
|
-
|
|
82
|
-
expect(providerRegistry.saveProviderPreferences).not.toHaveBeenCalled();
|
|
83
|
-
});
|
|
84
|
-
});
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
const { getProviderRateLimitedQuotas } = require('../src/utils/provider-rate-cache');
|
|
2
|
-
const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
|
|
3
|
-
|
|
4
|
-
describe('getProviderRateLimitedQuotas', () => {
|
|
5
|
-
let pm;
|
|
6
|
-
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
pm = new ProviderManager();
|
|
9
|
-
pm.clearAllRateLimits();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
pm.clearAllRateLimits();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test('returns rate-limited entry when ProviderManager has a limit', () => {
|
|
17
|
-
pm.markRateLimited('antigravity', undefined, 'Quota limit reached');
|
|
18
|
-
|
|
19
|
-
const defs = [{ id: 'antigravity' }, { id: 'vscode' }];
|
|
20
|
-
const map = getProviderRateLimitedQuotas(defs);
|
|
21
|
-
|
|
22
|
-
expect(map.has('antigravity')).toBe(true);
|
|
23
|
-
const q = map.get('antigravity');
|
|
24
|
-
expect(q).toHaveProperty('type', 'rate-limit');
|
|
25
|
-
expect(q).toHaveProperty('resetsAt');
|
|
26
|
-
});
|
|
27
|
-
});
|
package/jest.config.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"timestamp":"2025-12-28T01:17:10.393Z","type":"auto-mode-stop","reason":"startup","message":"Auto Mode stopped (startup)"}
|
package/repro_open.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
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
|
-
}
|
package/reproduce_issue.js
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
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/reset_provider_order.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
const { getDefaultProviderOrder, saveProviderPreferences, getProviderPreferences } = require('./src/utils/provider-registry');
|
|
2
|
-
const chalk = require('chalk');
|
|
3
|
-
|
|
4
|
-
async function resetOrder() {
|
|
5
|
-
try {
|
|
6
|
-
const defaultOrder = getDefaultProviderOrder();
|
|
7
|
-
console.log('New Default Order:', defaultOrder);
|
|
8
|
-
|
|
9
|
-
const currentPrefs = await getProviderPreferences();
|
|
10
|
-
console.log('Current User Order:', currentPrefs.order);
|
|
11
|
-
|
|
12
|
-
// Force update the order to match default
|
|
13
|
-
await saveProviderPreferences(defaultOrder, currentPrefs.enabled);
|
|
14
|
-
console.log(chalk.green('Successfully reset provider order to default (Cloud -> IDE -> Local).'));
|
|
15
|
-
} catch (error) {
|
|
16
|
-
console.error('Failed to reset order:', error);
|
|
17
|
-
process.exit(1);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
resetOrder();
|
package/scripts/README.md
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
# Auto Start Wrapper Script
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
The `auto-start-wrapper.sh` script provides **reliable Ctrl+C handling** for `vcm auto:start`.
|
|
6
|
-
|
|
7
|
-
## Problem
|
|
8
|
-
|
|
9
|
-
Due to a Node.js limitation, SIGINT (Ctrl+C) signals don't reliably reach the event loop when Node.js is blocked waiting for child processes (like Aider). **This means Ctrl+C does NOT work when running `vcm auto:start` directly.**
|
|
10
|
-
|
|
11
|
-
## Recommended Usage
|
|
12
|
-
|
|
13
|
-
**If you want Ctrl+C to work**, use the wrapper script instead of running `vcm auto:start` directly:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
# From anywhere (recommended)
|
|
17
|
-
~/.asdf/installs/nodejs/20.19.5/lib/node_modules/@vibecodingmachine/cli/scripts/auto-start-wrapper.sh
|
|
18
|
-
|
|
19
|
-
# Or if you know the package location
|
|
20
|
-
/path/to/vibecodingmachine/packages/cli/scripts/auto-start-wrapper.sh
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
### Creating a Convenient Alias
|
|
24
|
-
|
|
25
|
-
Add this to your `~/.zshrc` or `~/.bashrc`:
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
# Alias for vcm auto:start with Ctrl+C support
|
|
29
|
-
alias vcm-start='~/.asdf/installs/nodejs/20.19.5/lib/node_modules/@vibecodingmachine/cli/scripts/auto-start-wrapper.sh'
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Then you can simply run:
|
|
33
|
-
```bash
|
|
34
|
-
vcm-start
|
|
35
|
-
vcm-start --max-chats 10
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Solution
|
|
39
|
-
|
|
40
|
-
This wrapper script:
|
|
41
|
-
1. Runs `vcm auto:start` in the background
|
|
42
|
-
2. Monitors keyboard input in the foreground (in the wrapper's process)
|
|
43
|
-
3. When Ctrl+C, Esc, or 'x' is pressed, creates a `.stop` file
|
|
44
|
-
4. The running `vcm` process detects the file (via watchdog timer) and exits gracefully
|
|
45
|
-
|
|
46
|
-
## Usage
|
|
47
|
-
|
|
48
|
-
### Direct Usage
|
|
49
|
-
```bash
|
|
50
|
-
# From the project root
|
|
51
|
-
./packages/cli/scripts/auto-start-wrapper.sh
|
|
52
|
-
|
|
53
|
-
# Or with options
|
|
54
|
-
./packages/cli/scripts/auto-start-wrapper.sh --max-chats 5
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### How to Stop
|
|
58
|
-
- **Ctrl+C**: Stops the process (most common)
|
|
59
|
-
- **Esc key**: Stops the process
|
|
60
|
-
- **'x' key**: Stops the process
|
|
61
|
-
- **`vcm auto:stop`**: From another terminal
|
|
62
|
-
|
|
63
|
-
## How It Works
|
|
64
|
-
|
|
65
|
-
1. **Wrapper starts**: Spawns `node vcm auto:start` as a background process
|
|
66
|
-
2. **Wrapper monitors**: Uses `read -t 0.5 -n 1` to check for key presses every 500ms
|
|
67
|
-
3. **Key pressed**: Creates `~/.config/vibecodingmachine/.stop` file
|
|
68
|
-
4. **Watchdog detects**: The running vcm process has a watchdog that checks for this file every 500ms
|
|
69
|
-
5. **Graceful exit**: When detected, vcm kills Aider processes and exits the main loop
|
|
70
|
-
6. **Cleanup**: Wrapper removes the stop file and exits
|
|
71
|
-
|
|
72
|
-
## Alternative: Manual Stop File
|
|
73
|
-
|
|
74
|
-
You can also create the stop file manually:
|
|
75
|
-
```bash
|
|
76
|
-
# Create stop file
|
|
77
|
-
touch ~/.config/vibecodingmachine/.stop
|
|
78
|
-
|
|
79
|
-
# Or use the stop command
|
|
80
|
-
vcm auto:stop
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## Technical Details
|
|
84
|
-
|
|
85
|
-
### Watchdog Timer
|
|
86
|
-
The watchdog runs every 500ms in the Node.js process:
|
|
87
|
-
```javascript
|
|
88
|
-
const watchdog = setInterval(async () => {
|
|
89
|
-
if (await fs.pathExists(stopFilePath)) {
|
|
90
|
-
console.log('🛑 Stop signal detected (via .stop file)');
|
|
91
|
-
shouldExit = true;
|
|
92
|
-
exitReason = 'user-stop';
|
|
93
|
-
await fs.unlink(stopFilePath);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (shouldExit) {
|
|
97
|
-
clearInterval(watchdog);
|
|
98
|
-
aiderManager.killAllProcesses();
|
|
99
|
-
// Loop breaks naturally
|
|
100
|
-
}
|
|
101
|
-
}, 500);
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### Why Not Direct SIGINT?
|
|
105
|
-
Node.js SIGINT handlers aren't called when:
|
|
106
|
-
- `await` is blocked on a child process
|
|
107
|
-
- The event loop is not running (waiting for I/O)
|
|
108
|
-
- Child processes intercept signals first
|
|
109
|
-
|
|
110
|
-
The wrapper script solves this by handling signals at the shell level (outside Node.js).
|
|
111
|
-
|
|
112
|
-
## Testing
|
|
113
|
-
|
|
114
|
-
Test the wrapper with Ctrl+C:
|
|
115
|
-
```bash
|
|
116
|
-
./packages/cli/scripts/auto-start-wrapper.sh &
|
|
117
|
-
sleep 5
|
|
118
|
-
# Press Ctrl+C
|
|
119
|
-
# Should see: "🛑 Stop signal detected" and process exits
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
Test with stop command:
|
|
123
|
-
```bash
|
|
124
|
-
./packages/cli/scripts/auto-start-wrapper.sh &
|
|
125
|
-
sleep 5
|
|
126
|
-
vcm auto:stop
|
|
127
|
-
# Should see process exit within 2 seconds
|
|
128
|
-
```
|