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.
Files changed (41) hide show
  1. package/package.json +14 -5
  2. package/src/utils/interactive.js +53 -3
  3. package/.allnightai/REQUIREMENTS.md +0 -11
  4. package/.eslintrc.js +0 -16
  5. package/__tests__/antigravity-js-handler.test.js +0 -23
  6. package/__tests__/provider-manager.test.js +0 -84
  7. package/__tests__/provider-rate-cache.test.js +0 -27
  8. package/jest.config.js +0 -8
  9. package/logs/audit/2025-11-07.jsonl +0 -2
  10. package/logs/audit/2025-12-24.jsonl +0 -2
  11. package/logs/audit/2025-12-27.jsonl +0 -1
  12. package/logs/audit/2026-01-03.jsonl +0 -2
  13. package/repro_open.js +0 -13
  14. package/reproduce_issue.js +0 -160
  15. package/reset_provider_order.js +0 -21
  16. package/scripts/README.md +0 -128
  17. package/scripts/auto-start-wrapper.sh +0 -92
  18. package/scripts/convert-requirements.js +0 -35
  19. package/scripts/debug-parse.js +0 -24
  20. package/src/commands/auto.js.bak +0 -710
  21. package/src/utils/auto-mode-ui.js.bak.blessed +0 -207
  22. package/tests/antigravity-js-handler.test.js +0 -23
  23. package/tests/auto-mode.test.js +0 -37
  24. package/tests/config.test.js +0 -34
  25. package/tests/home-bootstrap.test.js +0 -76
  26. package/tests/integration/health-tracking.integration.test.js +0 -284
  27. package/tests/provider-manager.test.js +0 -92
  28. package/tests/rate-limit-display.test.js +0 -44
  29. package/tests/requirements-bullet-parsing.test.js +0 -15
  30. package/tests/requirements-converter.test.js +0 -42
  31. package/tests/requirements-heading-count.test.js +0 -27
  32. package/tests/requirements-legacy-parsing.test.js +0 -15
  33. package/tests/requirements-navigator-buildtree-await.test.js +0 -28
  34. package/tests/requirements-parse-integration.test.js +0 -44
  35. package/tests/wait-for-ide-completion.test.js +0 -56
  36. package/tests/wait-for-ide-quota-detection-cursor-screenshot.test.js +0 -61
  37. package/tests/wait-for-ide-quota-detection-cursor.test.js +0 -60
  38. package/tests/wait-for-ide-quota-detection-negative.test.js +0 -45
  39. package/tests/wait-for-ide-quota-detection.test.js +0 -59
  40. package/verify_fix.js +0 -36
  41. package/verify_ui.js +0 -38
@@ -1,207 +0,0 @@
1
- const blessed = require('blessed');
2
- const chalk = require('chalk');
3
-
4
- /**
5
- * Create and display a blessed-based UI for Auto Mode
6
- * Shows: persistent menu header, status card, and scrolling output
7
- * @param {object} options - UI configuration
8
- * @param {string} options.menuContent - Menu content to display in header
9
- * @param {function} options.onExit - Callback when user presses Ctrl+C or q
10
- * @returns {object} UI interface with methods to update status and append output
11
- */
12
- function createAutoModeUI(options = {}) {
13
- const { menuContent = '', onExit } = options;
14
-
15
- // Create screen
16
- const screen = blessed.screen({
17
- smartCSR: true,
18
- title: 'AllNightAI Auto Mode',
19
- fullUnicode: true,
20
- forceUnicode: true
21
- });
22
-
23
- // Clear screen and render immediately
24
- screen.clearRegion(0, screen.width, 0, screen.height);
25
-
26
- // Header box (menu) - use fixed height instead of shrink
27
- const headerHeight = 11; // Fixed height for header
28
- const header = blessed.box({
29
- top: 0,
30
- left: 0,
31
- width: '100%',
32
- height: headerHeight,
33
- content: menuContent,
34
- tags: true,
35
- border: {
36
- type: 'line'
37
- },
38
- style: {
39
- border: {
40
- fg: 'cyan'
41
- }
42
- }
43
- });
44
-
45
- // Status card (purple/magenta border like GUI)
46
- const statusCardHeight = 12;
47
- const statusCard = blessed.box({
48
- top: headerHeight,
49
- left: 0,
50
- width: '100%',
51
- height: statusCardHeight,
52
- tags: true,
53
- border: {
54
- type: 'round'
55
- },
56
- style: {
57
- border: {
58
- fg: 'magenta'
59
- }
60
- },
61
- label: ' Auto Mode Status '
62
- });
63
-
64
- // Output log (scrollable)
65
- const totalHeaderHeight = headerHeight + statusCardHeight;
66
- const outputLog = blessed.log({
67
- top: totalHeaderHeight,
68
- left: 0,
69
- width: '100%',
70
- height: `100%-${totalHeaderHeight}`,
71
- tags: true,
72
- border: {
73
- type: 'line'
74
- },
75
- style: {
76
- border: {
77
- fg: 'green'
78
- }
79
- },
80
- label: ' Aider Output ',
81
- scrollable: true,
82
- alwaysScroll: true,
83
- mouse: true,
84
- keys: true,
85
- vi: true,
86
- scrollbar: {
87
- ch: ' ',
88
- track: {
89
- bg: 'cyan'
90
- },
91
- style: {
92
- inverse: true
93
- }
94
- }
95
- });
96
-
97
- // Append all elements to screen
98
- screen.append(header);
99
- screen.append(statusCard);
100
- screen.append(outputLog);
101
-
102
- // Focus the output log for scrolling
103
- outputLog.focus();
104
-
105
- // Key bindings
106
- screen.key(['escape', 'q', 'C-c'], () => {
107
- if (onExit) {
108
- onExit();
109
- }
110
- screen.destroy();
111
- process.exit(0);
112
- });
113
-
114
- // Initial render
115
- screen.render();
116
-
117
- /**
118
- * Update the status card with new status information
119
- * @param {object} status - Status object
120
- * @param {string} status.requirement - Current requirement
121
- * @param {string} status.step - Current step (PREPARE, ACT, CLEAN UP, VERIFY, DONE)
122
- * @param {number} status.chatCount - Current chat count
123
- * @param {number|null} status.maxChats - Max chats or null for unlimited
124
- * @param {number} status.progress - Progress percentage (0-100)
125
- */
126
- function updateStatus(status) {
127
- const {
128
- requirement = 'No requirement loaded',
129
- step = 'UNKNOWN',
130
- chatCount = 0,
131
- maxChats = null,
132
- progress = 0
133
- } = status;
134
-
135
- // Step color mapping
136
- const stepColors = {
137
- 'PREPARE': '{cyan-fg}',
138
- 'ACT': '{yellow-fg}',
139
- 'CLEAN UP': '{magenta-fg}',
140
- 'VERIFY': '{blue-fg}',
141
- 'DONE': '{green-fg}',
142
- 'UNKNOWN': '{gray-fg}'
143
- };
144
-
145
- const stepColor = stepColors[step] || '{gray-fg}';
146
-
147
- // Progress bar
148
- const barWidth = 40;
149
- const filledWidth = Math.round((progress / 100) * barWidth);
150
- const emptyWidth = barWidth - filledWidth;
151
- const progressBar = '{green-fg}' + '█'.repeat(filledWidth) + '{/green-fg}' +
152
- '{gray-fg}' + '░'.repeat(emptyWidth) + '{/gray-fg}';
153
-
154
- // Chat counter
155
- const chatDisplay = maxChats
156
- ? `Chat ${chatCount}/${maxChats}`
157
- : `Chat ${chatCount} (unlimited)`;
158
-
159
- // Build card content
160
- const content = `
161
- {bold}📋 Current Requirement{/bold}
162
-
163
- ${requirement.length > 70 ? requirement.substring(0, 67) + '...' : requirement}
164
-
165
- {bold}🚦 Status:{/bold} ${stepColor}{bold}${step}{/bold}{/}
166
-
167
- ${progressBar} ${progress}%
168
-
169
- {gray-fg}${chatDisplay}{/gray-fg}
170
- `;
171
-
172
- statusCard.setContent(content);
173
- screen.render();
174
- }
175
-
176
- /**
177
- * Append a line to the output log
178
- * @param {string} line - Line to append
179
- */
180
- function appendOutput(line) {
181
- outputLog.log(line);
182
- screen.render();
183
- }
184
-
185
- /**
186
- * Clear the output log
187
- */
188
- function clearOutput() {
189
- outputLog.setContent('');
190
- screen.render();
191
- }
192
-
193
- return {
194
- screen,
195
- header,
196
- statusCard,
197
- outputLog,
198
- updateStatus,
199
- appendOutput,
200
- clearOutput,
201
- destroy: () => screen.destroy()
202
- };
203
- }
204
-
205
- module.exports = {
206
- createAutoModeUI
207
- };
@@ -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,37 +0,0 @@
1
- const os = require('os');
2
- const path = require('path');
3
- const fs = require('fs-extra');
4
-
5
- describe('auto-mode utils', () => {
6
- const tmpConfig = path.join(os.tmpdir(), `vibecodingmachine_test_config_${Date.now()}.json`);
7
- const tmpRepo = path.join(os.tmpdir(), `vibecodingmachine_test_repo_${Date.now()}`);
8
-
9
- beforeAll(async () => {
10
- process.env.VIBECODINGMACHINE_CONFIG_PATH = tmpConfig;
11
- await fs.ensureDir(path.join(tmpRepo, '.vibecodingmachine', 'temp'));
12
- const { setRepoPath } = require('../src/utils/config');
13
- await setRepoPath(tmpRepo);
14
- });
15
-
16
- afterAll(async () => {
17
- delete process.env.VIBECODINGMACHINE_CONFIG_PATH;
18
- await fs.remove(tmpConfig).catch(() => {});
19
- await fs.remove(tmpRepo).catch(() => {});
20
- });
21
-
22
- test('start/stop and status', async () => {
23
- const { startAutoMode, stopAutoMode, checkAutoModeStatus } = require('../src/utils/auto-mode');
24
- const { getAutoConfig } = require('../src/utils/config');
25
- await startAutoMode(tmpRepo, { ide: 'cursor' });
26
- let status = await checkAutoModeStatus();
27
- expect(status.running).toBe(true);
28
- expect(status.ide).toBe('cursor');
29
-
30
- await stopAutoMode();
31
- status = await checkAutoModeStatus();
32
- expect(status.running).toBe(false);
33
- });
34
- });
35
-
36
-
37
-
@@ -1,34 +0,0 @@
1
- const os = require('os');
2
- const path = require('path');
3
- const fs = require('fs-extra');
4
-
5
- describe('config utils', () => {
6
- const tmpConfig = path.join(os.tmpdir(), `vibecodingmachine_test_config_${Date.now()}.json`);
7
-
8
- beforeAll(() => {
9
- process.env.VIBECODINGMACHINE_CONFIG_PATH = tmpConfig;
10
- });
11
-
12
- afterAll(async () => {
13
- delete process.env.VIBECODINGMACHINE_CONFIG_PATH;
14
- await fs.remove(tmpConfig).catch(() => {});
15
- });
16
-
17
- test('set/get repo path roundtrip', async () => {
18
- const { getRepoPath, setRepoPath } = require('../src/utils/config');
19
- expect(await getRepoPath()).toBeNull();
20
- await setRepoPath('/tmp/repo');
21
- expect(await getRepoPath()).toBe('/tmp/repo');
22
- });
23
-
24
- test('set/get auto config roundtrip', async () => {
25
- const { getAutoConfig, setAutoConfig } = require('../src/utils/config');
26
- await setAutoConfig({ ide: 'cursor', maxChats: 10 });
27
- const cfg = await getAutoConfig();
28
- expect(cfg.ide).toBe('cursor');
29
- expect(cfg.maxChats).toBe(10);
30
- });
31
- });
32
-
33
-
34
-
@@ -1,76 +0,0 @@
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
- });
@@ -1,284 +0,0 @@
1
- /**
2
- * Integration tests for Health Tracking
3
- * Tests cross-component behavior and data persistence
4
- * @jest-environment node
5
- */
6
-
7
- const fs = require('fs-extra');
8
- const path = require('path');
9
- const os = require('os');
10
- const { IDEHealthTracker } = require('vibecodingmachine-core');
11
-
12
- describe('Health Tracking Integration', () => {
13
- let testStorageFile;
14
-
15
- beforeEach(() => {
16
- testStorageFile = path.join(os.tmpdir(), `integration-test-health-${Date.now()}.json`);
17
- });
18
-
19
- afterEach(async () => {
20
- if (await fs.pathExists(testStorageFile)) {
21
- await fs.remove(testStorageFile);
22
- }
23
- const backupFile = `${testStorageFile}.bak`;
24
- if (await fs.pathExists(backupFile)) {
25
- await fs.remove(backupFile);
26
- }
27
- });
28
-
29
- describe('Data Persistence', () => {
30
- it('should persist health data across tracker instances', async () => {
31
- // Create first tracker and record interactions
32
- const tracker1 = new IDEHealthTracker({ storageFile: testStorageFile });
33
-
34
- await tracker1.recordSuccess('cursor', 120000, {
35
- requirementId: 'req-001',
36
- continuationPromptsDetected: 1,
37
- });
38
- await tracker1.recordSuccess('cursor', 115000);
39
- await tracker1.recordFailure('windsurf', 'Timeout exceeded', {
40
- timeoutUsed: 1800000,
41
- });
42
- await tracker1.recordQuota('vscode', 'Monthly quota exceeded');
43
-
44
- // Save explicitly
45
- await tracker1.save();
46
-
47
- // Create second tracker and verify data loaded
48
- const tracker2 = new IDEHealthTracker({ storageFile: testStorageFile });
49
- await tracker2.load();
50
-
51
- const cursorMetrics = await tracker2.getHealthMetrics('cursor');
52
- const windsurfMetrics = await tracker2.getHealthMetrics('windsurf');
53
- const vscodeMetrics = await tracker2.getHealthMetrics('vscode');
54
-
55
- // Verify Cursor data
56
- expect(cursorMetrics.successCount).toBe(2);
57
- expect(cursorMetrics.failureCount).toBe(0);
58
- expect(cursorMetrics.averageResponseTime).toBeGreaterThan(0);
59
-
60
- // Verify Windsurf data
61
- expect(windsurfMetrics.successCount).toBe(0);
62
- expect(windsurfMetrics.failureCount).toBe(1);
63
-
64
- // Verify VS Code quota didn't increment counters
65
- expect(vscodeMetrics.successCount).toBe(0);
66
- expect(vscodeMetrics.failureCount).toBe(0);
67
- });
68
-
69
- // KNOWN LIMITATION: Concurrent writes with autoSave:false not currently supported
70
- // The current implementation writes to disk on every operation, which causes race
71
- // conditions when multiple operations run concurrently. This would require in-memory
72
- // caching to fix properly. In production, autoSave defaults to true with 500ms
73
- // debouncing, which prevents this issue.
74
- it.skip('should handle concurrent writes from same tracker', async () => {
75
- const tracker = new IDEHealthTracker({
76
- storageFile: testStorageFile,
77
- autoSave: false,
78
- });
79
-
80
- // Record multiple interactions rapidly
81
- const promises = [];
82
- for (let i = 0; i < 20; i++) {
83
- promises.push(tracker.recordSuccess('cursor', 120000 + i * 100));
84
- }
85
- await Promise.all(promises);
86
-
87
- await tracker.save();
88
-
89
- // Verify all were recorded
90
- const metrics = await tracker.getHealthMetrics('cursor');
91
- expect(metrics.successCount).toBe(20);
92
- });
93
-
94
- it('should create backup before overwriting', async () => {
95
- const tracker1 = new IDEHealthTracker({ storageFile: testStorageFile });
96
- await tracker1.recordSuccess('cursor', 120000);
97
- await tracker1.save();
98
-
99
- // Verify file exists
100
- const fileExists = await fs.pathExists(testStorageFile);
101
- expect(fileExists).toBe(true);
102
-
103
- // Record more data (should create backup)
104
- const tracker2 = new IDEHealthTracker({ storageFile: testStorageFile });
105
- await tracker2.recordSuccess('windsurf', 180000);
106
- await tracker2.save();
107
-
108
- // Verify backup was created
109
- const backupFile = `${testStorageFile}.bak`;
110
- const backupExists = await fs.pathExists(backupFile);
111
- expect(backupExists).toBe(true);
112
-
113
- // Verify backup contains original data
114
- const backupData = await fs.readJson(backupFile);
115
- expect(backupData.ides.cursor).toBeDefined();
116
- expect(backupData.ides.cursor.successCount).toBe(1);
117
- });
118
-
119
- it('should restore from backup on corrupted data', async () => {
120
- // Create valid data
121
- const tracker1 = new IDEHealthTracker({ storageFile: testStorageFile });
122
- await tracker1.recordSuccess('cursor', 120000);
123
- await tracker1.save();
124
-
125
- // Corrupt the main file
126
- await fs.writeFile(testStorageFile, 'invalid json {{{');
127
-
128
- // Try to load - should restore from backup
129
- const tracker2 = new IDEHealthTracker({ storageFile: testStorageFile });
130
- const metrics = await tracker2.getHealthMetrics('cursor');
131
-
132
- // Should have loaded from backup
133
- expect(metrics.successCount).toBe(1);
134
- });
135
- });
136
-
137
- describe('Multi-IDE Scenarios', () => {
138
- it('should track multiple IDEs independently', async () => {
139
- const tracker = new IDEHealthTracker({ storageFile: testStorageFile });
140
-
141
- // Record interactions for different IDEs
142
- await tracker.recordSuccess('cursor', 120000);
143
- await tracker.recordSuccess('cursor', 115000);
144
- await tracker.recordSuccess('windsurf', 200000);
145
- await tracker.recordFailure('vscode', 'Error');
146
- await tracker.recordQuota('github-copilot', 'Quota exceeded');
147
-
148
- await tracker.save();
149
-
150
- // Verify each IDE tracked separately
151
- const allMetrics = await tracker.getAllHealthMetrics();
152
- expect(allMetrics.size).toBe(4); // cursor, windsurf, vscode, github-copilot
153
-
154
- expect(allMetrics.get('cursor').successCount).toBe(2);
155
- expect(allMetrics.get('windsurf').successCount).toBe(1);
156
- expect(allMetrics.get('vscode').failureCount).toBe(1);
157
- expect(allMetrics.get('github-copilot').successCount).toBe(0);
158
- expect(allMetrics.get('github-copilot').failureCount).toBe(0);
159
- });
160
-
161
- it('should recommend best IDE based on success rate', async () => {
162
- const tracker = new IDEHealthTracker({ storageFile: testStorageFile });
163
-
164
- // Cursor: 80% success (8/10)
165
- for (let i = 0; i < 8; i++) {
166
- await tracker.recordSuccess('cursor', 120000);
167
- }
168
- for (let i = 0; i < 2; i++) {
169
- await tracker.recordFailure('cursor', 'Error');
170
- }
171
-
172
- // Windsurf: 60% success (6/10)
173
- for (let i = 0; i < 6; i++) {
174
- await tracker.recordSuccess('windsurf', 180000);
175
- }
176
- for (let i = 0; i < 4; i++) {
177
- await tracker.recordFailure('windsurf', 'Error');
178
- }
179
-
180
- // VS Code: 100% but only 2 interactions (below threshold)
181
- for (let i = 0; i < 2; i++) {
182
- await tracker.recordSuccess('vscode', 150000);
183
- }
184
-
185
- const recommended = await tracker.getRecommendedIDE({ minInteractions: 10 });
186
- expect(recommended).toBe('cursor');
187
- });
188
- });
189
-
190
- describe('EWMA Calculation Persistence', () => {
191
- it('should persist and recalculate EWMA correctly', async () => {
192
- const tracker1 = new IDEHealthTracker({ storageFile: testStorageFile });
193
-
194
- // Record response times with increasing pattern
195
- await tracker1.recordSuccess('cursor', 100000);
196
- await tracker1.recordSuccess('cursor', 110000);
197
- await tracker1.recordSuccess('cursor', 120000);
198
- await tracker1.save();
199
-
200
- const metrics1 = await tracker1.getHealthMetrics('cursor');
201
- const ewma1 = metrics1.averageResponseTime;
202
- expect(ewma1).toBeGreaterThan(100000);
203
- expect(ewma1).toBeLessThan(120000);
204
-
205
- // Load in new instance and add more data
206
- const tracker2 = new IDEHealthTracker({ storageFile: testStorageFile });
207
- await tracker2.load();
208
-
209
- await tracker2.recordSuccess('cursor', 130000);
210
- const metrics2 = await tracker2.getHealthMetrics('cursor');
211
- const ewma2 = metrics2.averageResponseTime;
212
-
213
- // EWMA should have increased
214
- expect(ewma2).toBeGreaterThan(ewma1);
215
- });
216
- });
217
-
218
- describe('Consecutive Failures Across Sessions', () => {
219
- it('should maintain consecutive failure count across restarts', async () => {
220
- const tracker1 = new IDEHealthTracker({ storageFile: testStorageFile });
221
-
222
- // Record 3 failures
223
- await tracker1.recordFailure('cursor', 'Error 1');
224
- await tracker1.recordFailure('cursor', 'Error 2');
225
- await tracker1.recordFailure('cursor', 'Error 3');
226
- await tracker1.save();
227
-
228
- // Load in new instance
229
- const tracker2 = new IDEHealthTracker({ storageFile: testStorageFile });
230
- await tracker2.load();
231
-
232
- const metrics = await tracker2.getHealthMetrics('cursor');
233
- expect(metrics.consecutiveFailures).toBe(3);
234
-
235
- // Add 2 more failures to trigger threshold
236
- let eventEmitted = false;
237
- tracker2.on('consecutive-failures', () => {
238
- eventEmitted = true;
239
- });
240
-
241
- await tracker2.recordFailure('cursor', 'Error 4');
242
- await tracker2.recordFailure('cursor', 'Error 5');
243
-
244
- expect(eventEmitted).toBe(true);
245
- });
246
-
247
- it('should reset consecutive failures on success across sessions', async () => {
248
- const tracker1 = new IDEHealthTracker({ storageFile: testStorageFile });
249
-
250
- // Record failures
251
- await tracker1.recordFailure('cursor', 'Error 1');
252
- await tracker1.recordFailure('cursor', 'Error 2');
253
- await tracker1.save();
254
-
255
- // Load in new instance and record success
256
- const tracker2 = new IDEHealthTracker({ storageFile: testStorageFile });
257
- await tracker2.load();
258
-
259
- await tracker2.recordSuccess('cursor', 120000);
260
-
261
- const metrics = await tracker2.getHealthMetrics('cursor');
262
- expect(metrics.consecutiveFailures).toBe(0);
263
- expect(metrics.successCount).toBe(1);
264
- expect(metrics.failureCount).toBe(2);
265
- });
266
- });
267
-
268
- describe('Timeout Configuration Persistence', () => {
269
- it('should persist timeout configuration', async () => {
270
- const tracker1 = new IDEHealthTracker({ storageFile: testStorageFile });
271
-
272
- // Record some data
273
- await tracker1.recordSuccess('cursor', 120000);
274
- await tracker1.save();
275
-
276
- // Verify timeout config persisted
277
- const data = await fs.readJson(testStorageFile);
278
- expect(data.timeoutConfig).toBeDefined();
279
- expect(data.timeoutConfig.mode).toBe('fixed');
280
- expect(data.timeoutConfig.defaultTimeout).toBe(1800000);
281
- expect(data.timeoutConfig.bufferPercentage).toBe(0.4);
282
- });
283
- });
284
- });