vibecodingmachine-cli 2026.3.10-1812 → 2026.3.14-1528
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/README.md +85 -85
- package/bin/vibecodingmachine.js +3 -0
- package/package.json +4 -3
- package/scripts/postinstall.js +161 -161
- package/src/commands/auth.js +100 -100
- package/src/commands/auto-direct.js +16 -5
- package/src/commands/auto-execution.js +25 -0
- package/src/commands/auto-requirement-management.js +8 -8
- package/src/commands/auto-status-helpers.js +5 -3
- package/src/commands/computers.js +318 -318
- package/src/commands/feature.js +123 -123
- package/src/commands/locale.js +72 -72
- package/src/commands/repo.js +163 -163
- package/src/commands/setup.js +93 -93
- package/src/commands/sync.js +287 -287
- package/src/index.js +5 -5
- package/src/utils/agent-selector.js +50 -50
- package/src/utils/asset-cleanup.js +60 -60
- package/src/utils/auto-mode-ansi-ui.js +237 -237
- package/src/utils/auto-mode-simple-ui.js +141 -141
- package/src/utils/copy-with-progress.js +167 -167
- package/src/utils/download-with-progress.js +84 -84
- package/src/utils/keyboard-handler.js +153 -153
- package/src/utils/kiro-installer.js +178 -178
- package/src/utils/logger.js +4 -4
- package/src/utils/persistent-header.js +114 -114
- package/src/utils/prompt-helper.js +63 -63
- package/src/utils/provider-checker/agent-checker.js +25 -1
- package/src/utils/provider-checker/agent-runner.js +115 -37
- package/src/utils/provider-checker/agents-manager.js +210 -0
- package/src/utils/provider-checker/provider-validator.js +5 -49
- package/src/utils/provider-checker/requirements-manager.js +86 -65
- package/src/utils/provider-checker/test-requirements.js +25 -17
- package/src/utils/status-card.js +121 -121
- package/src/utils/stdout-interceptor.js +127 -127
- package/src/utils/user-tracking.js +299 -299
|
@@ -1,64 +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
|
|
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
64
|
};
|
|
@@ -173,6 +173,7 @@ async function runAgentCheck(providerId, def, repoPath, resultFile, timeoutMs, s
|
|
|
173
173
|
*/
|
|
174
174
|
async function checkProvider(providerId, config = {}, repoPath, onProgress = null, signal = null) {
|
|
175
175
|
const { getProviderDefinition } = require('../provider-registry');
|
|
176
|
+
const { VERIFIED_HEADER } = require('./test-requirements');
|
|
176
177
|
console.log(`[AGENT CHECK] Checking provider: ${providerId} in repo: ${repoPath}`);
|
|
177
178
|
|
|
178
179
|
const def = getProviderDefinition(providerId);
|
|
@@ -191,6 +192,15 @@ async function checkProvider(providerId, config = {}, repoPath, onProgress = nul
|
|
|
191
192
|
// Add test requirement
|
|
192
193
|
addTestRequirement(reqPath, resultFile);
|
|
193
194
|
|
|
195
|
+
// Log initial status (requirement in PENDING section)
|
|
196
|
+
console.log(`[AGENT CHECK] Initial STATUS: PREPARING - Test requirement added to ${reqPath}`);
|
|
197
|
+
let initialContent = '';
|
|
198
|
+
try {
|
|
199
|
+
initialContent = fs.readFileSync(reqPath, 'utf8');
|
|
200
|
+
} catch (err) {
|
|
201
|
+
console.log(`[AGENT CHECK] Warning: Could not read requirements file: ${err.message}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
194
204
|
try {
|
|
195
205
|
// Choose timeout based on provider type
|
|
196
206
|
const timeoutMs = def.type === 'cli' ? DIRECT_TIMEOUT_MS : IDE_TIMEOUT_MS;
|
|
@@ -205,12 +215,26 @@ async function checkProvider(providerId, config = {}, repoPath, onProgress = nul
|
|
|
205
215
|
fs.unlinkSync(resultFile);
|
|
206
216
|
}
|
|
207
217
|
|
|
218
|
+
// If we reached "DONE" status, the agent is communicating successfully
|
|
219
|
+
// The automation layer works - text was sent and processed
|
|
220
|
+
if (result.status === 'success') {
|
|
221
|
+
console.log(`[AGENT CHECK] ✅ Test PASSED: Agent reached DONE status - communication verified`);
|
|
222
|
+
return {
|
|
223
|
+
...result,
|
|
224
|
+
requirementLeftPending: false
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
console.log(`[AGENT CHECK] ❌ Test FAILED: Agent check did not reach DONE status`);
|
|
208
229
|
return {
|
|
209
|
-
|
|
230
|
+
status: 'error',
|
|
231
|
+
message: 'Agent check failed: did not reach DONE status',
|
|
232
|
+
checkedAt: result.checkedAt,
|
|
210
233
|
requirementLeftPending: false
|
|
211
234
|
};
|
|
212
235
|
} catch (error) {
|
|
213
236
|
// Ensure cleanup on error
|
|
237
|
+
console.log(`[AGENT CHECK] Error during check: ${error.message}`);
|
|
214
238
|
removeTestRequirement(reqPath);
|
|
215
239
|
if (fs.existsSync(resultFile)) {
|
|
216
240
|
fs.unlinkSync(resultFile);
|
|
@@ -7,7 +7,7 @@ const chokidar = require('chokidar');
|
|
|
7
7
|
|
|
8
8
|
const { NODE_EXECUTABLE } = require('./node-detector');
|
|
9
9
|
const { IDE_INFO, isIDERunning, openIDEApp } = require('./ide-manager');
|
|
10
|
-
const { getResultFilePath,
|
|
10
|
+
const { getResultFilePath, getAgentsFilePath, updateAgentStatus, getAgentStatus } = require('./agents-manager');
|
|
11
11
|
|
|
12
12
|
// Timeout for direct LLM round-trips via auto:direct (ms)
|
|
13
13
|
const DIRECT_TIMEOUT_MS = 60000;
|
|
@@ -15,7 +15,6 @@ const DIRECT_TIMEOUT_MS = 60000;
|
|
|
15
15
|
const IDE_TIMEOUT_MS = 90000;
|
|
16
16
|
|
|
17
17
|
const CLI_ENTRY_POINT = path.resolve(path.join(__dirname, '../../../bin/vibecodingmachine.js'));
|
|
18
|
-
const TEST_REQ_TITLE = 'VCM agent connectivity check';
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
20
|
* Run an agent for 1 iteration and watch for the result file to be written.
|
|
@@ -34,11 +33,6 @@ async function runAgentCheck(providerId, def, repoPath, resultFile, timeoutMs, s
|
|
|
34
33
|
// Remove stale result file so we detect a fresh write
|
|
35
34
|
try { fs.unlinkSync(resultFile); } catch { }
|
|
36
35
|
|
|
37
|
-
// Calculate requirements path early so it's available in the Promise scope
|
|
38
|
-
const os = require('os');
|
|
39
|
-
const hostname = os.hostname();
|
|
40
|
-
const requirementsPath = path.join(repoPath, `REQUIREMENTS-${hostname}.md`);
|
|
41
|
-
|
|
42
36
|
// For IDE providers: verify the IDE is running/installed, install if needed
|
|
43
37
|
let ideLaunchNote = '';
|
|
44
38
|
if (def.type === 'ide') {
|
|
@@ -52,7 +46,7 @@ async function runAgentCheck(providerId, def, repoPath, resultFile, timeoutMs, s
|
|
|
52
46
|
ideLaunchNote = ` (VCM launched ${info.app})`;
|
|
53
47
|
} else {
|
|
54
48
|
// Failed to launch on macOS - check if installed
|
|
55
|
-
const { IDEInstaller } = require('
|
|
49
|
+
const { IDEInstaller } = require('../../../../electron-app/src/main/ide-installer');
|
|
56
50
|
const installer = new IDEInstaller();
|
|
57
51
|
const ideKey = def.ide || providerId;
|
|
58
52
|
const checkResult = await installer.checkIDEInstallation(ideKey);
|
|
@@ -67,7 +61,7 @@ async function runAgentCheck(providerId, def, repoPath, resultFile, timeoutMs, s
|
|
|
67
61
|
}
|
|
68
62
|
// Windows: check if installed, install if possible, launch if installed
|
|
69
63
|
else if (process.platform === 'win32') {
|
|
70
|
-
const { IDEInstaller } = require('
|
|
64
|
+
const { IDEInstaller } = require('../../../../electron-app/src/main/ide-installer');
|
|
71
65
|
const installer = new IDEInstaller();
|
|
72
66
|
const ideKey = def.ide || providerId;
|
|
73
67
|
const ideConfig = IDEInstaller.IDE_CONFIG[ideKey];
|
|
@@ -147,7 +141,7 @@ async function runAgentCheck(providerId, def, repoPath, resultFile, timeoutMs, s
|
|
|
147
141
|
}
|
|
148
142
|
}
|
|
149
143
|
|
|
150
|
-
return new Promise((resolve) => {
|
|
144
|
+
return new Promise(async (resolve) => {
|
|
151
145
|
let resolved = false;
|
|
152
146
|
let child = null;
|
|
153
147
|
let output = '';
|
|
@@ -163,12 +157,32 @@ async function runAgentCheck(providerId, def, repoPath, resultFile, timeoutMs, s
|
|
|
163
157
|
clearInterval(cancelCheckInterval);
|
|
164
158
|
try { if (child) child.kill(); } catch { }
|
|
165
159
|
|
|
166
|
-
//
|
|
160
|
+
// Update agent status based on result
|
|
167
161
|
try {
|
|
168
|
-
|
|
169
|
-
|
|
162
|
+
if (result.status === 'success') {
|
|
163
|
+
// Status already set to 'operational' by the IDE agent
|
|
164
|
+
console.log(`[AGENT CHECK] ${providerId} check passed - status is 'operational'`);
|
|
165
|
+
} else if (result.rateLimited) {
|
|
166
|
+
updateAgentStatus(providerId, 'rate_limited', {
|
|
167
|
+
rateLimitResume: result.rateLimitResume
|
|
168
|
+
});
|
|
169
|
+
console.log(`[AGENT CHECK] Set ${providerId} status to 'rate_limited'`);
|
|
170
|
+
} else {
|
|
171
|
+
updateAgentStatus(providerId, 'error', {
|
|
172
|
+
error: result.message
|
|
173
|
+
});
|
|
174
|
+
console.log(`[AGENT CHECK] Set ${providerId} status to 'error'`);
|
|
175
|
+
}
|
|
170
176
|
} catch (err) {
|
|
171
|
-
console.warn(`[AGENT CHECK] Could not
|
|
177
|
+
console.warn(`[AGENT CHECK] Could not update agent status: ${err.message}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Notify UI that check is done
|
|
181
|
+
if (onProgress) {
|
|
182
|
+
const statusMessage = result.status === 'success'
|
|
183
|
+
? 'Check completed successfully'
|
|
184
|
+
: (result.rateLimited ? 'Rate limited' : result.message);
|
|
185
|
+
onProgress(providerId, 'done', result.status !== 'success' ? result.message : null, statusMessage);
|
|
172
186
|
}
|
|
173
187
|
|
|
174
188
|
resolve(result);
|
|
@@ -191,20 +205,31 @@ async function runAgentCheck(providerId, def, repoPath, resultFile, timeoutMs, s
|
|
|
191
205
|
watcher.on('add', checkResult);
|
|
192
206
|
watcher.on('change', checkResult);
|
|
193
207
|
|
|
194
|
-
//
|
|
195
|
-
|
|
208
|
+
// Watch agents.json for status changes to 'operational'
|
|
209
|
+
// Use debouncing to prevent multiple rapid status checks
|
|
210
|
+
let debounceTimer = null;
|
|
211
|
+
const agentsFilePath = getAgentsFilePath();
|
|
212
|
+
requirementsWatcher = chokidar.watch(agentsFilePath, { persistent: true, ignoreInitial: true });
|
|
196
213
|
requirementsWatcher.on('change', () => {
|
|
214
|
+
// Clear existing debounce timer
|
|
215
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
216
|
+
|
|
217
|
+
// Set new timer (50ms debounce - fast response while avoiding duplicate reads)
|
|
218
|
+
debounceTimer = setTimeout(() => {
|
|
197
219
|
try {
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
220
|
+
const agentStatus = getAgentStatus(providerId);
|
|
221
|
+
console.log(`[AGENT CHECK] Current status for ${providerId}: ${agentStatus.status}`);
|
|
222
|
+
|
|
223
|
+
// Check if agent status is 'operational'
|
|
224
|
+
if (agentStatus.status === 'operational') {
|
|
225
|
+
console.log(`[AGENT CHECK] Detected 'operational' status in agents.json for ${providerId}`);
|
|
226
|
+
if (onProgress) onProgress(providerId, 'response_detected', null, 'Status updated to operational.');
|
|
227
|
+
done({ status: 'success', message: 'Agent updated status to operational', checkedAt });
|
|
204
228
|
}
|
|
205
229
|
} catch (err) {
|
|
206
|
-
console.warn(`[AGENT CHECK] Error reading
|
|
230
|
+
console.warn(`[AGENT CHECK] Error reading agents.json: ${err.message}`);
|
|
207
231
|
}
|
|
232
|
+
}, 50); // End of debounce setTimeout - reduced from 300ms to 50ms for faster response
|
|
208
233
|
});
|
|
209
234
|
|
|
210
235
|
const timeout = setTimeout(() => {
|
|
@@ -245,14 +270,68 @@ async function runAgentCheck(providerId, def, repoPath, resultFile, timeoutMs, s
|
|
|
245
270
|
onProgress(providerId, 'awaiting', null, 'Awaiting response...');
|
|
246
271
|
}
|
|
247
272
|
|
|
248
|
-
|
|
273
|
+
// For IDE providers: send connectivity test directly using Windows automation
|
|
274
|
+
// Don't spawn auto:start since it expects requirements files (which we removed)
|
|
249
275
|
if (def.type === 'ide') {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
276
|
+
try {
|
|
277
|
+
const { generateConnectivityTestMessage } = require('./agents-manager');
|
|
278
|
+
const { WindowsAutomationManager } = require('vibecodingmachine-core');
|
|
279
|
+
|
|
280
|
+
console.log(`[AGENT CHECK] Sending connectivity test directly to ${providerId}`);
|
|
281
|
+
|
|
282
|
+
// Get the test message
|
|
283
|
+
const testMessage = generateConnectivityTestMessage(def.name || providerId, providerId);
|
|
284
|
+
|
|
285
|
+
// Send directly to IDE using Windows automation
|
|
286
|
+
const automation = new WindowsAutomationManager();
|
|
287
|
+
|
|
288
|
+
// Call the appropriate method based on provider
|
|
289
|
+
let sendResult;
|
|
290
|
+
try {
|
|
291
|
+
switch(providerId.toLowerCase()) {
|
|
292
|
+
case 'windsurf':
|
|
293
|
+
sendResult = await automation.sendTextToWindsurf(testMessage);
|
|
294
|
+
break;
|
|
295
|
+
case 'cursor':
|
|
296
|
+
sendResult = await automation.sendTextToCursor(testMessage);
|
|
297
|
+
break;
|
|
298
|
+
default:
|
|
299
|
+
sendResult = { success: false, error: `No automation method for ${providerId}` };
|
|
300
|
+
}
|
|
301
|
+
} catch (sendErr) {
|
|
302
|
+
sendResult = { success: false, error: sendErr.message };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (!sendResult || !sendResult.success) {
|
|
306
|
+
done({
|
|
307
|
+
status: 'error',
|
|
308
|
+
message: `Failed to send text to IDE: ${sendResult?.error || 'Unknown error'}`,
|
|
309
|
+
checkedAt
|
|
310
|
+
});
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
console.log(`[AGENT CHECK] Successfully sent connectivity test to ${providerId}`);
|
|
315
|
+
console.log(`[AGENT CHECK] Waiting for IDE to update agents.json status to 'operational'...`);
|
|
316
|
+
|
|
317
|
+
// Don't spawn child process - just wait for file watcher to detect status change
|
|
318
|
+
return;
|
|
319
|
+
|
|
320
|
+
} catch (err) {
|
|
321
|
+
console.error(`[AGENT CHECK] Error sending connectivity test: ${err.message}`);
|
|
322
|
+
done({ status: 'error', message: `Error sending test: ${err.message}`, checkedAt });
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// For direct LLM providers: still use auto:direct
|
|
328
|
+
let args;
|
|
329
|
+
if (def.type !== 'ide') {
|
|
255
330
|
args = [CLI_ENTRY_POINT, 'auto:direct', '--provider', providerId, '--max-chats', '1'];
|
|
331
|
+
} else {
|
|
332
|
+
// IDE handling moved above - this shouldn't be reached
|
|
333
|
+
done({ status: 'error', message: 'IDE handling logic error', checkedAt });
|
|
334
|
+
return;
|
|
256
335
|
}
|
|
257
336
|
|
|
258
337
|
// Verify CLI entry point exists and is readable
|
|
@@ -292,15 +371,13 @@ async function runAgentCheck(providerId, def, repoPath, resultFile, timeoutMs, s
|
|
|
292
371
|
console.log(`[AGENT CHECK] CWD: ${repoPath}`);
|
|
293
372
|
console.log(`[AGENT CHECK] Result file: ${resultFile}`);
|
|
294
373
|
|
|
295
|
-
//
|
|
296
|
-
|
|
297
|
-
// (requirementsPath already calculated at function start)
|
|
298
|
-
console.log(`[AGENT CHECK] Using requirements file: ${requirementsPath}`);
|
|
374
|
+
// Set agent status to 'checking' in agents.json before sending test
|
|
375
|
+
console.log(`[AGENT CHECK] Setting ${providerId} status to 'checking' in agents.json`);
|
|
299
376
|
try {
|
|
300
|
-
|
|
301
|
-
console.log(`[AGENT CHECK]
|
|
377
|
+
updateAgentStatus(providerId, 'checking');
|
|
378
|
+
console.log(`[AGENT CHECK] Set ${providerId} status to 'checking'`);
|
|
302
379
|
} catch (err) {
|
|
303
|
-
console.warn(`[AGENT CHECK] Could not
|
|
380
|
+
console.warn(`[AGENT CHECK] Could not set agent status: ${err.message}`);
|
|
304
381
|
}
|
|
305
382
|
|
|
306
383
|
// Prepare environment - inherit all process.env and add NODE_PATH if needed
|
|
@@ -366,7 +443,8 @@ async function runAgentCheck(providerId, def, repoPath, resultFile, timeoutMs, s
|
|
|
366
443
|
|
|
367
444
|
// Check for DONE status in output (indicates successful completion)
|
|
368
445
|
if (code === 0 && output.includes('Status: ✅ DONE') && output.includes('Requirement complete')) {
|
|
369
|
-
console.log(`[AGENT CHECK] Detected
|
|
446
|
+
console.log(`[AGENT CHECK] Detected DONE in console output for ${providerId}`);
|
|
447
|
+
console.log(`[AGENT CHECK] ✅ Test PASSED: Agent reached DONE status - communication verified`);
|
|
370
448
|
if (onProgress) onProgress(providerId, 'response_detected', null, 'Auto mode completed successfully.');
|
|
371
449
|
done({ status: 'success', message: 'Agent completed requirement successfully', checkedAt });
|
|
372
450
|
return;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parse relative time string (e.g., "5h 30m", "2h", "45m") to absolute Date
|
|
9
|
+
* @param {string} relativeTime - Time string like "5h 30m"
|
|
10
|
+
* @returns {Date} Absolute date when the time will occur
|
|
11
|
+
*/
|
|
12
|
+
function parseRelativeTimeToAbsolute(relativeTime) {
|
|
13
|
+
const now = new Date();
|
|
14
|
+
let totalMinutes = 0;
|
|
15
|
+
|
|
16
|
+
// Parse hours
|
|
17
|
+
const hoursMatch = relativeTime.match(/(\d+)h/);
|
|
18
|
+
if (hoursMatch) {
|
|
19
|
+
totalMinutes += parseInt(hoursMatch[1]) * 60;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Parse minutes
|
|
23
|
+
const minutesMatch = relativeTime.match(/(\d+)m/);
|
|
24
|
+
if (minutesMatch) {
|
|
25
|
+
totalMinutes += parseInt(minutesMatch[1]);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Parse seconds
|
|
29
|
+
const secondsMatch = relativeTime.match(/(\d+)s/);
|
|
30
|
+
if (secondsMatch) {
|
|
31
|
+
totalMinutes += parseInt(secondsMatch[1]) / 60;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return new Date(now.getTime() + totalMinutes * 60 * 1000);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get the path to the result file used by agent checks
|
|
39
|
+
*/
|
|
40
|
+
function getResultFilePath(repoPath) {
|
|
41
|
+
return path.join(repoPath, '.vibecodingmachine', 'temp', 'TEMP_agent_check.txt');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get the path to the user's agents.json file in ~/.vibecodingmachine/
|
|
46
|
+
*/
|
|
47
|
+
function getAgentsFilePath() {
|
|
48
|
+
const homeDir = os.homedir();
|
|
49
|
+
const vcmDir = path.join(homeDir, '.vibecodingmachine');
|
|
50
|
+
|
|
51
|
+
// Ensure directory exists
|
|
52
|
+
if (!fs.existsSync(vcmDir)) {
|
|
53
|
+
fs.mkdirSync(vcmDir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return path.join(vcmDir, 'agents.json');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Read the agents.json file
|
|
61
|
+
* Returns the agents object or creates a new one if it doesn't exist
|
|
62
|
+
*/
|
|
63
|
+
function readAgentsFile() {
|
|
64
|
+
const agentsPath = getAgentsFilePath();
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
if (fs.existsSync(agentsPath)) {
|
|
68
|
+
const content = fs.readFileSync(agentsPath, 'utf8');
|
|
69
|
+
return JSON.parse(content);
|
|
70
|
+
}
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.warn(`[AGENTS] Error reading agents file: ${err.message}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Return default structure
|
|
76
|
+
return { agents: {} };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Write the agents.json file
|
|
81
|
+
*/
|
|
82
|
+
function writeAgentsFile(agentsData) {
|
|
83
|
+
const agentsPath = getAgentsFilePath();
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
fs.writeFileSync(agentsPath, JSON.stringify(agentsData, null, 2), 'utf8');
|
|
87
|
+
return true;
|
|
88
|
+
} catch (err) {
|
|
89
|
+
console.error(`[AGENTS] Error writing agents file: ${err.message}`);
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Update agent status
|
|
96
|
+
* @param {string} agentId - Agent ID (e.g., 'windsurf', 'cursor', 'antigravity')
|
|
97
|
+
* @param {string} status - Status: 'unknown', 'checking', 'operational', 'rate_limited', 'error'
|
|
98
|
+
* @param {object} metadata - Optional metadata (e.g., { rateLimitResume: '5m30s', error: 'message' })
|
|
99
|
+
*/
|
|
100
|
+
function updateAgentStatus(agentId, status, metadata = {}) {
|
|
101
|
+
const agentsData = readAgentsFile();
|
|
102
|
+
|
|
103
|
+
// Ensure agents object exists
|
|
104
|
+
if (!agentsData.agents) {
|
|
105
|
+
agentsData.agents = {};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Ensure agent entry exists
|
|
109
|
+
if (!agentsData.agents[agentId]) {
|
|
110
|
+
agentsData.agents[agentId] = {
|
|
111
|
+
id: agentId,
|
|
112
|
+
status: 'unknown'
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Update status
|
|
117
|
+
agentsData.agents[agentId].status = status;
|
|
118
|
+
agentsData.agents[agentId].lastChecked = new Date().toISOString();
|
|
119
|
+
|
|
120
|
+
// Add metadata
|
|
121
|
+
if (status === 'rate_limited' && metadata.rateLimitResume) {
|
|
122
|
+
// Convert relative time (e.g., "5h 30m") to absolute timestamp
|
|
123
|
+
const resumeTime = parseRelativeTimeToAbsolute(metadata.rateLimitResume);
|
|
124
|
+
agentsData.agents[agentId].rateLimitResumeAt = resumeTime.toISOString();
|
|
125
|
+
agentsData.agents[agentId].rateLimitResume = metadata.rateLimitResume; // Keep original for reference
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (status === 'error' && metadata.error) {
|
|
129
|
+
agentsData.agents[agentId].error = metadata.error;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Clear rate limit fields if no longer rate limited
|
|
133
|
+
if (status !== 'rate_limited') {
|
|
134
|
+
delete agentsData.agents[agentId].rateLimitResumeAt;
|
|
135
|
+
delete agentsData.agents[agentId].rateLimitResume;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Write file
|
|
139
|
+
writeAgentsFile(agentsData);
|
|
140
|
+
|
|
141
|
+
console.log(`[AGENTS] Updated ${agentId} status to: ${status}`);
|
|
142
|
+
|
|
143
|
+
return agentsData.agents[agentId];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get agent status
|
|
148
|
+
* @param {string} agentId - Agent ID
|
|
149
|
+
* @returns {object} Agent status object
|
|
150
|
+
*/
|
|
151
|
+
function getAgentStatus(agentId) {
|
|
152
|
+
const agentsData = readAgentsFile();
|
|
153
|
+
|
|
154
|
+
if (agentsData.agents && agentsData.agents[agentId]) {
|
|
155
|
+
return agentsData.agents[agentId];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
id: agentId,
|
|
160
|
+
status: 'unknown'
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get all agents with their statuses
|
|
166
|
+
* @returns {object} All agents
|
|
167
|
+
*/
|
|
168
|
+
function getAllAgents() {
|
|
169
|
+
const agentsData = readAgentsFile();
|
|
170
|
+
return agentsData.agents || {};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Generate the connectivity test message for IDE agents
|
|
175
|
+
* @param {string} agentName - Human-readable agent name (e.g., 'Windsurf', 'Cursor')
|
|
176
|
+
* @param {string} agentId - Agent ID (e.g., 'windsurf', 'cursor')
|
|
177
|
+
* @returns {string} Test message to send to IDE
|
|
178
|
+
*/
|
|
179
|
+
function generateConnectivityTestMessage(agentName, agentId) {
|
|
180
|
+
const agentsPath = getAgentsFilePath();
|
|
181
|
+
const relativeAgentsPath = agentsPath.replace(os.homedir(), '~');
|
|
182
|
+
|
|
183
|
+
return `# VCM Agent Connectivity Check
|
|
184
|
+
|
|
185
|
+
ACTION REQUIRED - VCM Connectivity Test:
|
|
186
|
+
|
|
187
|
+
VCM is testing communication with your IDE. To complete this test, please:
|
|
188
|
+
|
|
189
|
+
1. Open the file at: ${relativeAgentsPath}
|
|
190
|
+
2. Find the "${agentId}" entry
|
|
191
|
+
3. Change the "status" field from "checking" to "operational"
|
|
192
|
+
4. Save the file
|
|
193
|
+
|
|
194
|
+
Example:
|
|
195
|
+
Before: "status": "checking"
|
|
196
|
+
After: "status": "operational"
|
|
197
|
+
|
|
198
|
+
VCM will detect the change within seconds and complete the test.`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = {
|
|
202
|
+
getResultFilePath,
|
|
203
|
+
getAgentsFilePath,
|
|
204
|
+
readAgentsFile,
|
|
205
|
+
writeAgentsFile,
|
|
206
|
+
updateAgentStatus,
|
|
207
|
+
getAgentStatus,
|
|
208
|
+
getAllAgents,
|
|
209
|
+
generateConnectivityTestMessage
|
|
210
|
+
};
|