vibecodingmachine-cli 2026.1.3-2209 → 2026.1.23-1010
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/__tests__/antigravity-js-handler.test.js +23 -0
- package/__tests__/provider-manager.test.js +84 -0
- package/__tests__/provider-rate-cache.test.js +27 -0
- package/bin/vibecodingmachine.js +8 -0
- package/package.json +2 -2
- package/reset_provider_order.js +21 -0
- package/scripts/convert-requirements.js +35 -0
- package/scripts/debug-parse.js +24 -0
- package/src/commands/auto-direct.js +679 -120
- package/src/commands/auto.js +200 -45
- package/src/commands/ide.js +108 -3
- package/src/commands/requirements-remote.js +10 -1
- package/src/commands/status.js +39 -1
- package/src/utils/antigravity-js-handler.js +13 -4
- package/src/utils/auth.js +37 -13
- package/src/utils/compliance-check.js +10 -0
- package/src/utils/config.js +29 -1
- package/src/utils/date-formatter.js +44 -0
- package/src/utils/interactive.js +1006 -537
- package/src/utils/kiro-js-handler.js +188 -0
- package/src/utils/provider-rate-cache.js +31 -0
- package/src/utils/provider-registry.js +42 -1
- package/src/utils/requirements-converter.js +107 -0
- package/src/utils/requirements-parser.js +144 -0
- package/tests/antigravity-js-handler.test.js +23 -0
- package/tests/integration/health-tracking.integration.test.js +284 -0
- package/tests/provider-manager.test.js +92 -0
- package/tests/rate-limit-display.test.js +44 -0
- package/tests/requirements-bullet-parsing.test.js +15 -0
- package/tests/requirements-converter.test.js +42 -0
- package/tests/requirements-heading-count.test.js +27 -0
- package/tests/requirements-legacy-parsing.test.js +15 -0
- package/tests/requirements-parse-integration.test.js +44 -0
- package/tests/wait-for-ide-completion.test.js +56 -0
- package/tests/wait-for-ide-quota-detection-cursor-screenshot.test.js +61 -0
- package/tests/wait-for-ide-quota-detection-cursor.test.js +60 -0
- package/tests/wait-for-ide-quota-detection-negative.test.js +45 -0
- package/tests/wait-for-ide-quota-detection.test.js +59 -0
- package/verify_fix.js +36 -0
- package/verify_ui.js +38 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
|
|
5
|
+
const { waitForIdeCompletion } = require('../src/commands/auto-direct');
|
|
6
|
+
|
|
7
|
+
jest.mock('vibecodingmachine-core', () => ({
|
|
8
|
+
getRequirementsPath: jest.fn(),
|
|
9
|
+
detectLocale: () => 'en',
|
|
10
|
+
setLocale: () => {},
|
|
11
|
+
t: (k, o) => (o ? JSON.stringify(o) : k)
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const { getRequirementsPath } = require('vibecodingmachine-core');
|
|
15
|
+
const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
|
|
16
|
+
|
|
17
|
+
describe('waitForIdeCompletion - Cursor screenshot message', () => {
|
|
18
|
+
let tmpDir;
|
|
19
|
+
let reqDir;
|
|
20
|
+
let reqPath;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vcm-test-'));
|
|
24
|
+
reqDir = path.join(tmpDir, '.vibecodingmachine');
|
|
25
|
+
fs.mkdirSync(reqDir, { recursive: true });
|
|
26
|
+
reqPath = path.join(reqDir, 'REQUIREMENTS-testhost.md');
|
|
27
|
+
|
|
28
|
+
getRequirementsPath.mockResolvedValue(reqPath);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
getRequirementsPath.mockReset();
|
|
33
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch (e) { }
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('detects Cursor usage limit from screenshot-like message', async () => {
|
|
37
|
+
jest.setTimeout(20000);
|
|
38
|
+
|
|
39
|
+
// Simulate REQUIREMENTS file content matching the screenshot (will be written after watcher starts)
|
|
40
|
+
const msg = `# Requirements\n\nYou've hit your usage limit\nGet Cursor Pro for more Agent usage, unlimited Tab, and more.`;
|
|
41
|
+
|
|
42
|
+
const requirementText = 'TEST A: Do something';
|
|
43
|
+
|
|
44
|
+
const markSpy = jest.spyOn(ProviderManager.prototype, 'markRateLimited');
|
|
45
|
+
|
|
46
|
+
const promise = waitForIdeCompletion(tmpDir, requirementText, 'cursor', 10000);
|
|
47
|
+
|
|
48
|
+
// After a short delay, write the quota message into the file
|
|
49
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
50
|
+
fs.writeFileSync(reqPath, msg);
|
|
51
|
+
|
|
52
|
+
const result = await promise;
|
|
53
|
+
|
|
54
|
+
expect(result.success).toBe(false);
|
|
55
|
+
expect(result.rateLimited).toBe(true);
|
|
56
|
+
expect(result.providerRateLimited).toBe('cursor');
|
|
57
|
+
expect(markSpy).toHaveBeenCalledWith('cursor', undefined, expect.stringContaining("You've hit your usage limit"));
|
|
58
|
+
|
|
59
|
+
markSpy.mockRestore();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const { waitForIdeCompletion } = require('../src/commands/auto-direct');
|
|
5
|
+
|
|
6
|
+
jest.mock('vibecodingmachine-core', () => ({
|
|
7
|
+
getRequirementsPath: jest.fn(),
|
|
8
|
+
detectLocale: () => 'en',
|
|
9
|
+
setLocale: () => {},
|
|
10
|
+
t: (k, o) => (o ? JSON.stringify(o) : k)
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
const { getRequirementsPath } = require('vibecodingmachine-core');
|
|
14
|
+
const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
|
|
15
|
+
|
|
16
|
+
describe('waitForIdeCompletion generic rate limit detection (Cursor)', () => {
|
|
17
|
+
let tmpDir;
|
|
18
|
+
let reqDir;
|
|
19
|
+
let reqPath;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vcm-test-'));
|
|
23
|
+
reqDir = path.join(tmpDir, '.vibecodingmachine');
|
|
24
|
+
fs.mkdirSync(reqDir, { recursive: true });
|
|
25
|
+
reqPath = path.join(reqDir, 'REQUIREMENTS-testhost.md');
|
|
26
|
+
|
|
27
|
+
getRequirementsPath.mockResolvedValue(reqPath);
|
|
28
|
+
|
|
29
|
+
fs.writeFileSync(reqPath, '# Requirements\n\n- TEST A: Do something\n');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
getRequirementsPath.mockReset();
|
|
34
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch (e) { }
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('detects Cursor quota message in file and marks provider rate-limited', async () => {
|
|
38
|
+
// Spy on ProviderManager.markRateLimited
|
|
39
|
+
const markSpy = jest.spyOn(ProviderManager.prototype, 'markRateLimited');
|
|
40
|
+
|
|
41
|
+
const requirementText = 'TEST A: Do something';
|
|
42
|
+
|
|
43
|
+
const promise = waitForIdeCompletion(tmpDir, requirementText, 'cursor', 10000);
|
|
44
|
+
|
|
45
|
+
// After a short delay, write a quota message into the file that mimics Cursor-like output
|
|
46
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
47
|
+
|
|
48
|
+
const quotaMsg = `# Requirements\n\nError: Usage cap reached for model. Try again in 15m.\n`;
|
|
49
|
+
fs.writeFileSync(reqPath, quotaMsg);
|
|
50
|
+
|
|
51
|
+
const result = await promise;
|
|
52
|
+
|
|
53
|
+
expect(result.success).toBe(false);
|
|
54
|
+
expect(result.rateLimited).toBe(true);
|
|
55
|
+
expect(result.providerRateLimited).toBe('cursor');
|
|
56
|
+
expect(markSpy).toHaveBeenCalledWith('cursor', undefined, expect.stringContaining('Usage cap'));
|
|
57
|
+
|
|
58
|
+
markSpy.mockRestore();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const { waitForIdeCompletion } = require('../src/commands/auto-direct');
|
|
5
|
+
|
|
6
|
+
jest.mock('vibecodingmachine-core', () => ({
|
|
7
|
+
getRequirementsPath: jest.fn(),
|
|
8
|
+
detectLocale: () => 'en',
|
|
9
|
+
setLocale: () => {},
|
|
10
|
+
t: (k, o) => (o ? JSON.stringify(o) : k)
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
const { getRequirementsPath } = require('vibecodingmachine-core');
|
|
14
|
+
|
|
15
|
+
describe('waitForIdeCompletion negative rate limit detection', () => {
|
|
16
|
+
let tmpDir;
|
|
17
|
+
let reqDir;
|
|
18
|
+
let reqPath;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vcm-test-'));
|
|
22
|
+
reqDir = path.join(tmpDir, '.vibecodingmachine');
|
|
23
|
+
fs.mkdirSync(reqDir, { recursive: true });
|
|
24
|
+
reqPath = path.join(reqDir, 'REQUIREMENTS-testhost.md');
|
|
25
|
+
|
|
26
|
+
getRequirementsPath.mockResolvedValue(reqPath);
|
|
27
|
+
|
|
28
|
+
fs.writeFileSync(reqPath, '# Requirements\n\n### R169: When rate limit hit, that agent should have been update in the agents list to display when the quota is reset.\n');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
getRequirementsPath.mockReset();
|
|
33
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch (e) { }
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('does not treat requirement headings that mention rate limits as a provider rate limit', async () => {
|
|
37
|
+
const requirementText = 'R169: When rate limit hit';
|
|
38
|
+
|
|
39
|
+
const result = await waitForIdeCompletion(tmpDir, requirementText, 'cursor', 500);
|
|
40
|
+
|
|
41
|
+
// With a very short timeout, it should simply time out rather than treat the heading as a quota
|
|
42
|
+
expect(result.success).toBe(false);
|
|
43
|
+
expect(result.rateLimited).not.toBe(true);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const { waitForIdeCompletion } = require('../src/commands/auto-direct');
|
|
5
|
+
|
|
6
|
+
jest.mock('vibecodingmachine-core', () => ({
|
|
7
|
+
getRequirementsPath: jest.fn(),
|
|
8
|
+
detectLocale: () => 'en',
|
|
9
|
+
setLocale: () => {},
|
|
10
|
+
t: (k, o) => (o ? JSON.stringify(o) : k)
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
const { getRequirementsPath } = require('vibecodingmachine-core');
|
|
14
|
+
const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
|
|
15
|
+
|
|
16
|
+
describe('waitForIdeCompletion rate limit detection', () => {
|
|
17
|
+
let tmpDir;
|
|
18
|
+
let reqDir;
|
|
19
|
+
let reqPath;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vcm-test-'));
|
|
23
|
+
reqDir = path.join(tmpDir, '.vibecodingmachine');
|
|
24
|
+
fs.mkdirSync(reqDir, { recursive: true });
|
|
25
|
+
reqPath = path.join(reqDir, 'REQUIREMENTS-testhost.md');
|
|
26
|
+
|
|
27
|
+
getRequirementsPath.mockResolvedValue(reqPath);
|
|
28
|
+
|
|
29
|
+
fs.writeFileSync(reqPath, '# Requirements\n\n- TEST A: Do something\n');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
getRequirementsPath.mockReset();
|
|
34
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch (e) { }
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('detects Antigravity quota message in file and marks provider rate-limited', async () => {
|
|
38
|
+
// Spy on ProviderManager.markRateLimited
|
|
39
|
+
const markSpy = jest.spyOn(ProviderManager.prototype, 'markRateLimited');
|
|
40
|
+
|
|
41
|
+
const requirementText = 'TEST A: Do something';
|
|
42
|
+
|
|
43
|
+
const promise = waitForIdeCompletion(tmpDir, requirementText, 'antigravity', 10000);
|
|
44
|
+
|
|
45
|
+
// After a short delay, write a quota message into the file that mimics Antigravity output
|
|
46
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
47
|
+
|
|
48
|
+
const quotaMsg = `# Requirements\n\nError Agent execution terminated due to error.\nYou have reached the quota limit for this model. You can resume using this model at 1/19/2026, 4:07:27 PM.\n`;
|
|
49
|
+
fs.writeFileSync(reqPath, quotaMsg);
|
|
50
|
+
|
|
51
|
+
const result = await promise;
|
|
52
|
+
|
|
53
|
+
expect(result.success).toBe(false);
|
|
54
|
+
expect(result.antigravityRateLimited).toBe(true);
|
|
55
|
+
expect(markSpy).toHaveBeenCalledWith('antigravity', undefined, expect.stringContaining('You have reached the quota limit'));
|
|
56
|
+
|
|
57
|
+
markSpy.mockRestore();
|
|
58
|
+
});
|
|
59
|
+
});
|
package/verify_fix.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const interactive = require('./src/utils/interactive');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
|
|
4
|
+
console.log(chalk.green('Interactive module loaded successfully.'));
|
|
5
|
+
|
|
6
|
+
// Basic syntax and load check passed.
|
|
7
|
+
|
|
8
|
+
// Now let's try to verify the quota logic if possible.
|
|
9
|
+
// Since the function is internal, we can't unit test it easily from outside without mocking.
|
|
10
|
+
// However, we confirmed the syntax is valid.
|
|
11
|
+
|
|
12
|
+
// We can try to mock the quota object structure to ensure our logic snippet is valid JS.
|
|
13
|
+
function testQuotaLogic() {
|
|
14
|
+
const quota = { type: 'rate-limit', remaining: 0, resetsAt: Date.now() + 10000 };
|
|
15
|
+
// The fixed code:
|
|
16
|
+
let isExceeded = false;
|
|
17
|
+
if (typeof quota.isExceeded === 'function') {
|
|
18
|
+
isExceeded = quota.isExceeded();
|
|
19
|
+
} else if (quota.remaining !== undefined) {
|
|
20
|
+
isExceeded = quota.remaining <= 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (isExceeded !== true) {
|
|
24
|
+
console.error('Logic test failed: Expected isExceeded to be true for remaining: 0');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
console.log('Logic test passed: Safe quota check handles plain objects.');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
testQuotaLogic();
|
|
32
|
+
console.log('Verification passed: Module loads and quota logic is safe.');
|
|
33
|
+
} catch (e) {
|
|
34
|
+
console.error('Verification failed:', e);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
package/verify_ui.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
// Mock translation
|
|
3
|
+
const t = (key) => key === 'provider.legend' ? 'Legend' : key;
|
|
4
|
+
|
|
5
|
+
// Mock provider data
|
|
6
|
+
const defs = [
|
|
7
|
+
{ id: 'groq', name: 'Groq', type: 'direct' },
|
|
8
|
+
{ id: 'anthropic', name: 'Anthropic', type: 'direct' },
|
|
9
|
+
{ id: 'ollama', name: 'Ollama', type: 'direct' },
|
|
10
|
+
{ id: 'cursor', name: 'Cursor', type: 'ide' }
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
// Mock renderer snippet (simplified logic from interactive.js)
|
|
14
|
+
console.log('Rendering Legend...');
|
|
15
|
+
console.log(chalk.gray(' ' + t('provider.legend') + ': 🖥️ IDE ☁️ Cloud LLM 🐢 Local LLM'));
|
|
16
|
+
|
|
17
|
+
console.log('Rendering Items...');
|
|
18
|
+
defs.forEach((def, idx) => {
|
|
19
|
+
let typeIcon = '☁️ ';
|
|
20
|
+
if (def.type === 'ide') typeIcon = '🖥️ ';
|
|
21
|
+
else if (def.id === 'ollama') typeIcon = '🐢 ';
|
|
22
|
+
|
|
23
|
+
const prefix = ' ';
|
|
24
|
+
const statusEmoji = '🟢';
|
|
25
|
+
const line = `${prefix} ${typeIcon} ${statusEmoji} ${idx + 1}. ${def.name}`;
|
|
26
|
+
console.log(line);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
console.log('\nVerification:');
|
|
30
|
+
// Simple assertion check
|
|
31
|
+
const output = ` Legend: 🖥️ IDE ☁️ Cloud LLM 🐢 Local LLM
|
|
32
|
+
☁️ 🟢 1. Groq
|
|
33
|
+
☁️ 🟢 2. Anthropic
|
|
34
|
+
🐢 🟢 3. Ollama
|
|
35
|
+
🖥️ 🟢 4. Cursor`;
|
|
36
|
+
|
|
37
|
+
console.log('Expected output snippet:');
|
|
38
|
+
console.log(output);
|