vibecodingmachine-cli 2026.2.20-438 → 2026.2.26-1739
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/auth/auth-compliance.js +126 -0
- package/bin/cli-program.js +104 -0
- package/bin/cli-setup.js +52 -0
- package/bin/commands/agent-commands.js +310 -0
- package/bin/commands/auto-commands.js +70 -0
- package/bin/commands/command-aliases.js +118 -0
- package/bin/commands/repo-commands.js +39 -0
- package/bin/commands/rui-commands.js +152 -0
- package/bin/config/cli-config.js +394 -0
- package/bin/init/environment-setup.js +84 -0
- package/bin/update/update-checker.js +126 -0
- package/bin/vibecodingmachine-new.js +50 -0
- package/bin/vibecodingmachine.js +29 -663
- package/package.json +8 -2
- package/src/commands/agents/add.js +277 -0
- package/src/commands/agents/check.js +380 -0
- package/src/commands/agents/list.js +471 -0
- package/src/commands/agents/remove.js +351 -0
- package/src/commands/analyze-file-sizes.js +428 -0
- package/src/commands/auto-direct/code-processor.js +282 -0
- package/src/commands/auto-direct/file-scanner.js +266 -0
- package/src/commands/auto-direct/provider-config.js +178 -0
- package/src/commands/auto-direct/provider-manager.js +219 -0
- package/src/commands/auto-direct/requirement-manager.js +172 -0
- package/src/commands/auto-direct/status-display.js +91 -0
- package/src/commands/auto-direct/utils.js +106 -0
- package/src/commands/auto-direct.js +875 -488
- package/src/commands/auto-execution.js +342 -0
- package/src/commands/auto-provider-management.js +102 -0
- package/src/commands/auto-requirement-management.js +161 -0
- package/src/commands/auto-status-helpers.js +141 -0
- package/src/commands/auto.js +105 -5155
- package/src/commands/check-compliance.js +536 -0
- package/src/commands/continuous-scan.js +119 -0
- package/src/commands/ide.js +16 -4
- package/src/commands/refactor-file.js +486 -0
- package/src/commands/requirements.js +301 -2
- package/src/commands/timeout.js +290 -0
- package/src/trui/TruiInterface.js +108 -0
- package/src/trui/agents/AgentInterface.js +580 -0
- package/src/utils/antigravity-installer.js +60 -6
- package/src/utils/clarification-actions.js +290 -0
- package/src/utils/config.js +123 -2
- package/src/utils/first-run.js +5 -5
- package/src/utils/ide-handlers.js +212 -0
- package/src/utils/interactive/clarification-actions.js +348 -0
- package/src/utils/interactive/core-ui.js +265 -0
- package/src/utils/interactive/file-backup.js +237 -0
- package/src/utils/interactive/file-import-export.js +305 -0
- package/src/utils/interactive/file-operations.js +49 -0
- package/src/utils/interactive/file-validation.js +276 -0
- package/src/utils/interactive/interactive-prompts.js +480 -0
- package/src/utils/interactive/requirement-actions.js +127 -0
- package/src/utils/interactive/requirement-crud.js +356 -0
- package/src/utils/interactive/requirements-navigation.js +286 -0
- package/src/utils/interactive.js +390 -3459
- package/src/utils/provider-checker/agent-checker.js +250 -0
- package/src/utils/provider-checker/agent-runner.js +450 -0
- package/src/utils/provider-checker/cli-installer.js +123 -0
- package/src/utils/provider-checker/cli-utils.js +15 -0
- package/src/utils/provider-checker/format-utils.js +32 -0
- package/src/utils/provider-checker/ide-manager.js +72 -0
- package/src/utils/provider-checker/ide-utils.js +71 -0
- package/src/utils/provider-checker/node-detector.js +56 -0
- package/src/utils/provider-checker/node-utils.js +61 -0
- package/src/utils/provider-checker/process-spawn.js +22 -0
- package/src/utils/provider-checker/process-utils.js +37 -0
- package/src/utils/provider-checker/provider-validator.js +160 -0
- package/src/utils/provider-checker/quota-checker.js +54 -0
- package/src/utils/provider-checker/quota-detector.js +44 -0
- package/src/utils/provider-checker/requirements-manager.js +94 -0
- package/src/utils/provider-checker/test-requirements.js +95 -0
- package/src/utils/provider-checker/time-formatter.js +18 -0
- package/src/utils/provider-checker-new.js +14 -0
- package/src/utils/provider-checker.js +12 -407
- package/src/utils/provider-checkers/ide-manager.js +128 -0
- package/src/utils/provider-checkers/node-executable-finder.js +51 -0
- package/src/utils/provider-checkers/provider-checker-core.js +172 -0
- package/src/utils/provider-checkers/provider-checker-main.js +107 -0
- package/src/utils/provider-manager.js +60 -4
- package/src/utils/provider-registry.js +26 -3
- package/src/utils/provider-utils.js +173 -0
- package/src/utils/quota-detectors.js +212 -0
- package/src/utils/requirement-action-handlers.js +288 -0
- package/src/utils/requirement-actions/clarification-actions.js +229 -0
- package/src/utils/requirement-actions/confirmation-prompts.js +93 -0
- package/src/utils/requirement-actions/file-operations.js +92 -0
- package/src/utils/requirement-actions/helpers.js +40 -0
- package/src/utils/requirement-actions/requirement-operations.js +335 -0
- package/src/utils/requirement-actions.js +46 -856
- package/src/utils/requirement-file-operations.js +259 -0
- package/src/utils/requirement-helpers.js +128 -0
- package/src/utils/requirement-management.js +279 -0
- package/src/utils/requirement-navigation.js +146 -0
- package/src/utils/requirement-organization.js +271 -0
- package/src/utils/simple-trui.js +75 -1
- package/src/utils/trui-navigation.js +28 -2
- package/src/utils/trui-req-tree.js +196 -11
- package/src/utils/trui-specifications.js +31 -1
- package/src/utils/interactive-backup.js +0 -5664
- package/src/utils/trui-provider-manager.js +0 -182
|
@@ -1,410 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// Direct CLI providers that can be auto-installed via npm
|
|
16
|
-
const CLI_AUTO_INSTALL = {
|
|
17
|
-
'cline': { cmd: 'cline', pkg: '@cline/cli' },
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Check if a CLI command is available in PATH.
|
|
22
|
-
*/
|
|
23
|
-
function isCLIAvailable(cmd) {
|
|
24
|
-
try {
|
|
25
|
-
execSync(`which "${cmd}"`, { stdio: 'ignore' });
|
|
26
|
-
return true;
|
|
27
|
-
} catch {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Install a CLI tool globally via npm and wait for it to complete.
|
|
34
|
-
* Returns { installed: bool, note: string }.
|
|
35
|
-
*/
|
|
36
|
-
async function installCLI(pkg, cmd) {
|
|
37
|
-
return new Promise((resolve) => {
|
|
38
|
-
const proc = spawn('npm', ['install', '-g', pkg], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
39
|
-
let out = '';
|
|
40
|
-
proc.stdout.on('data', d => { out += d.toString(); });
|
|
41
|
-
proc.stderr.on('data', d => { out += d.toString(); });
|
|
42
|
-
proc.on('error', () => resolve({ installed: false, note: `Failed to run npm install -g ${pkg}` }));
|
|
43
|
-
proc.on('close', (code) => {
|
|
44
|
-
if (code === 0 && isCLIAvailable(cmd)) {
|
|
45
|
-
resolve({ installed: true, note: `Installed ${pkg} via npm` });
|
|
46
|
-
} else {
|
|
47
|
-
resolve({ installed: false, note: `npm install -g ${pkg} exited (${code})` });
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Map provider id → { process: macOS process name, app: macOS app name for `open -a` }
|
|
54
|
-
const IDE_INFO = {
|
|
55
|
-
windsurf: { process: 'Windsurf', app: 'Windsurf' },
|
|
56
|
-
cursor: { process: 'Cursor', app: 'Cursor' },
|
|
57
|
-
antigravity: { process: 'Antigravity', app: 'Antigravity' },
|
|
58
|
-
kiro: { process: 'Code', app: 'Kiro' },
|
|
59
|
-
'github-copilot': { process: 'Code', app: 'Visual Studio Code' },
|
|
60
|
-
'amazon-q': { process: 'Code', app: 'Visual Studio Code' },
|
|
61
|
-
cline: { process: 'Code', app: 'Visual Studio Code' },
|
|
62
|
-
vscode: { process: 'Code', app: 'Visual Studio Code' },
|
|
63
|
-
replit: { process: null, app: null }, // web-based
|
|
3
|
+
// Import all provider checker modules
|
|
4
|
+
const { checkProvider, checkAllProviders } = require('./provider-checker/provider-validator');
|
|
5
|
+
const { formatCheckedAt } = require('./provider-checker/time-formatter');
|
|
6
|
+
const { DIRECT_TIMEOUT_MS, IDE_TIMEOUT_MS } = require('./provider-checker/agent-runner');
|
|
7
|
+
|
|
8
|
+
// Re-export the main API
|
|
9
|
+
module.exports = {
|
|
10
|
+
checkProvider,
|
|
11
|
+
checkAllProviders,
|
|
12
|
+
formatCheckedAt,
|
|
13
|
+
DIRECT_TIMEOUT_MS,
|
|
14
|
+
IDE_TIMEOUT_MS
|
|
64
15
|
};
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Returns true if the IDE process is currently running.
|
|
68
|
-
*/
|
|
69
|
-
function isIDERunning(processName) {
|
|
70
|
-
if (!processName || process.platform !== 'darwin') return true; // assume running on non-mac
|
|
71
|
-
try {
|
|
72
|
-
execSync(`pgrep -x "${processName}"`, { stdio: 'ignore' });
|
|
73
|
-
return true;
|
|
74
|
-
} catch {
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Open the IDE using `open -a <app> <repoPath>` (macOS).
|
|
81
|
-
* Returns true when the launch command was sent.
|
|
82
|
-
*/
|
|
83
|
-
async function openIDEApp(appName, repoPath) {
|
|
84
|
-
if (!appName || process.platform !== 'darwin') return false;
|
|
85
|
-
try {
|
|
86
|
-
const safeRepo = repoPath ? ` "${repoPath.replace(/"/g, '\\"')}"` : '';
|
|
87
|
-
execSync(`open -a "${appName}"${safeRepo}`, { stdio: 'ignore' });
|
|
88
|
-
// Give it a few seconds to start before the automation begins
|
|
89
|
-
await new Promise(r => setTimeout(r, 5000));
|
|
90
|
-
return true;
|
|
91
|
-
} catch {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const TEST_REQ_TITLE = 'VCM agent connectivity check';
|
|
97
|
-
const PENDING_HEADER = '## ⏳ Requirements not yet completed';
|
|
98
|
-
const VERIFIED_HEADER = '## ✅ Verified by AI';
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Return the absolute path to the result file used by the check.
|
|
102
|
-
*/
|
|
103
|
-
function getResultFilePath(repoPath) {
|
|
104
|
-
return path.join(repoPath, '.vibecodingmachine', 'temp', 'TEMP_agent_check.txt');
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Add the test requirement at the TOP of the pending section.
|
|
109
|
-
* Idempotent — if it's already there, do nothing.
|
|
110
|
-
*/
|
|
111
|
-
function addTestRequirement(reqPath, resultFile) {
|
|
112
|
-
let content = '';
|
|
113
|
-
try { content = fs.readFileSync(reqPath, 'utf8'); } catch {}
|
|
114
|
-
|
|
115
|
-
// Already present?
|
|
116
|
-
if (content.includes(TEST_REQ_TITLE)) return;
|
|
117
|
-
|
|
118
|
-
const testBlock = `### 1. ${TEST_REQ_TITLE}
|
|
119
|
-
🚦 Current Status: PREPARE
|
|
120
|
-
Create a new file at the following absolute path: ${resultFile}
|
|
121
|
-
The file must contain exactly this text on the first line: VCM_CHECK_OK
|
|
122
|
-
Do not modify any other files. This is an automated connectivity test.
|
|
123
|
-
|
|
124
|
-
`;
|
|
125
|
-
|
|
126
|
-
const idx = content.indexOf(PENDING_HEADER);
|
|
127
|
-
if (idx !== -1) {
|
|
128
|
-
const afterHeader = idx + PENDING_HEADER.length;
|
|
129
|
-
// Insert after the header, then existing content follows
|
|
130
|
-
const before = content.slice(0, afterHeader);
|
|
131
|
-
const after = content.slice(afterHeader).replace(/^\n+/, '');
|
|
132
|
-
content = before + '\n\n' + testBlock + after;
|
|
133
|
-
} else {
|
|
134
|
-
// No pending section — prepend one
|
|
135
|
-
content = PENDING_HEADER + '\n\n' + testBlock + '\n' + (content || VERIFIED_HEADER + '\n');
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
fs.writeFileSync(reqPath, content, 'utf8');
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Remove the test requirement from REQUIREMENTS.md regardless of which section it's in.
|
|
143
|
-
* Also cleans up triple-blank lines left behind.
|
|
144
|
-
*/
|
|
145
|
-
function removeTestRequirement(reqPath) {
|
|
146
|
-
let content = '';
|
|
147
|
-
try { content = fs.readFileSync(reqPath, 'utf8'); } catch { return; }
|
|
148
|
-
|
|
149
|
-
if (!content.includes(TEST_REQ_TITLE)) return;
|
|
150
|
-
|
|
151
|
-
const lines = content.split('\n');
|
|
152
|
-
|
|
153
|
-
// Find the ### header line for this requirement
|
|
154
|
-
let blockStart = -1;
|
|
155
|
-
for (let i = 0; i < lines.length; i++) {
|
|
156
|
-
if (lines[i].includes(TEST_REQ_TITLE) || (lines[i].startsWith('###') && i + 1 < lines.length && lines[i + 1].includes(TEST_REQ_TITLE))) {
|
|
157
|
-
// Walk back to find the ### line
|
|
158
|
-
blockStart = lines[i].startsWith('###') ? i : i - 1;
|
|
159
|
-
if (blockStart < 0) blockStart = i;
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (blockStart === -1) return;
|
|
165
|
-
|
|
166
|
-
// Find end of block (next ### or ##, or EOF)
|
|
167
|
-
let blockEnd = blockStart + 1;
|
|
168
|
-
while (blockEnd < lines.length && !lines[blockEnd].startsWith('###') && !lines[blockEnd].startsWith('## ')) {
|
|
169
|
-
blockEnd++;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
lines.splice(blockStart, blockEnd - blockStart);
|
|
173
|
-
const cleaned = lines.join('\n').replace(/\n{3,}/g, '\n\n').trimEnd() + '\n';
|
|
174
|
-
fs.writeFileSync(reqPath, cleaned, 'utf8');
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Spawn a process and wait for it to exit. Returns { code, output }.
|
|
179
|
-
*/
|
|
180
|
-
function spawnAndWait(cmd, args, opts = {}) {
|
|
181
|
-
return new Promise((resolve) => {
|
|
182
|
-
let output = '';
|
|
183
|
-
const child = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'], ...opts });
|
|
184
|
-
child.stdout && child.stdout.on('data', d => { output += d.toString(); });
|
|
185
|
-
child.stderr && child.stderr.on('data', d => { output += d.toString(); });
|
|
186
|
-
child.on('error', () => resolve({ code: -1, output }));
|
|
187
|
-
child.on('exit', (code) => resolve({ code, output }));
|
|
188
|
-
return child;
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Run an agent for 1 iteration and watch for the result file to be written.
|
|
194
|
-
* For IDE providers: ensure the IDE is open first, launching it if needed.
|
|
195
|
-
* Returns { status, message, checkedAt }.
|
|
196
|
-
*/
|
|
197
|
-
async function runAgentCheck(providerId, def, repoPath, resultFile, timeoutMs, signal = null) {
|
|
198
|
-
const checkedAt = new Date().toISOString();
|
|
199
|
-
|
|
200
|
-
// Ensure temp dir exists
|
|
201
|
-
fs.mkdirSync(path.dirname(resultFile), { recursive: true });
|
|
202
|
-
// Remove stale result file so we detect a fresh write
|
|
203
|
-
try { fs.unlinkSync(resultFile); } catch {}
|
|
204
|
-
|
|
205
|
-
// For IDE providers: verify the IDE is running, open it if not
|
|
206
|
-
let ideLaunchNote = '';
|
|
207
|
-
if (def.type === 'ide') {
|
|
208
|
-
const info = IDE_INFO[providerId] || IDE_INFO[def.ide] || null;
|
|
209
|
-
if (info && info.process) {
|
|
210
|
-
if (!isIDERunning(info.process)) {
|
|
211
|
-
if (info.app) {
|
|
212
|
-
await openIDEApp(info.app, repoPath);
|
|
213
|
-
ideLaunchNote = ` (VCM launched ${info.app})`;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return new Promise((resolve) => {
|
|
220
|
-
let resolved = false;
|
|
221
|
-
let child = null;
|
|
222
|
-
|
|
223
|
-
function done(result) {
|
|
224
|
-
if (resolved) return;
|
|
225
|
-
resolved = true;
|
|
226
|
-
try { watcher.close(); } catch {}
|
|
227
|
-
clearTimeout(timeout);
|
|
228
|
-
clearInterval(cancelCheckInterval);
|
|
229
|
-
try { if (child) child.kill(); } catch {}
|
|
230
|
-
resolve(result);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function checkResult() {
|
|
234
|
-
if (!resolved && fs.existsSync(resultFile)) {
|
|
235
|
-
try {
|
|
236
|
-
const content = fs.readFileSync(resultFile, 'utf8');
|
|
237
|
-
if (content.includes('VCM_CHECK_OK')) {
|
|
238
|
-
done({ status: 'success', message: 'End-to-end check passed', checkedAt });
|
|
239
|
-
}
|
|
240
|
-
} catch {}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const watcher = chokidar.watch(resultFile, { persistent: true, ignoreInitial: false, disableGlobbing: true });
|
|
245
|
-
watcher.on('add', checkResult);
|
|
246
|
-
watcher.on('change', checkResult);
|
|
247
|
-
|
|
248
|
-
const timeout = setTimeout(() => {
|
|
249
|
-
// Diagnose: is the IDE process still running?
|
|
250
|
-
let diagnosis = '';
|
|
251
|
-
if (def.type === 'ide') {
|
|
252
|
-
const info = IDE_INFO[providerId] || IDE_INFO[def.ide] || null;
|
|
253
|
-
if (info && info.process) {
|
|
254
|
-
diagnosis = isIDERunning(info.process)
|
|
255
|
-
? ` — ${info.app || info.process} is running but did not respond`
|
|
256
|
-
: ` — ${info.app || info.process} is not running`;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
done({ status: 'error', message: `No response within ${timeoutMs / 1000}s${ideLaunchNote}${diagnosis}`, checkedAt });
|
|
260
|
-
}, timeoutMs);
|
|
261
|
-
|
|
262
|
-
// Periodically check for cancellation signal
|
|
263
|
-
const cancelCheckInterval = setInterval(() => {
|
|
264
|
-
if (signal && signal.cancelled) {
|
|
265
|
-
done({ status: 'error', message: 'Check cancelled by user', checkedAt });
|
|
266
|
-
}
|
|
267
|
-
}, 1000); // Check every second
|
|
268
|
-
|
|
269
|
-
let args;
|
|
270
|
-
if (def.type === 'ide') {
|
|
271
|
-
args = [CLI_ENTRY_POINT, 'auto:start', '--ide', def.ide || providerId, '--max-chats', '1'];
|
|
272
|
-
if (def.defaultModel) args.push('--ide-model', String(def.defaultModel));
|
|
273
|
-
if (def.extension) args.push('--extension', String(def.extension));
|
|
274
|
-
} else {
|
|
275
|
-
// Direct LLM: use auto:direct --provider to force this specific provider
|
|
276
|
-
args = [CLI_ENTRY_POINT, 'auto:direct', '--provider', providerId, '--max-chats', '1'];
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
child = spawn(process.execPath, args, {
|
|
280
|
-
cwd: repoPath,
|
|
281
|
-
env: process.env,
|
|
282
|
-
stdio: ['ignore', 'pipe', 'pipe']
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
let output = '';
|
|
286
|
-
child.stdout.on('data', d => { output += d.toString(); });
|
|
287
|
-
child.stderr.on('data', d => { output += d.toString(); });
|
|
288
|
-
|
|
289
|
-
child.on('error', (err) => {
|
|
290
|
-
done({ status: 'error', message: `Failed to start automation: ${err.message}`, checkedAt });
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
child.on('exit', (code) => {
|
|
294
|
-
if (resolved) return;
|
|
295
|
-
// Check one more time after process exits
|
|
296
|
-
checkResult();
|
|
297
|
-
if (!resolved) {
|
|
298
|
-
const tail = output.slice(-300).trim().replace(/\n/g, ' ');
|
|
299
|
-
done({ status: 'error', message: `Automation exited (code ${code})${tail ? ': ' + tail : ''}`, checkedAt });
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Check a single provider end-to-end using the actual REQUIREMENTS.md.
|
|
307
|
-
*
|
|
308
|
-
* Flow:
|
|
309
|
-
* 1. Add test requirement to top of REQUIREMENTS.md
|
|
310
|
-
* 2. Run the agent for 1 iteration
|
|
311
|
-
* 3. On success, requirement is auto-moved to Verified by the agent; remove it
|
|
312
|
-
* 4. On failure, leave requirement in pending for the NEXT agent (caller handles cleanup)
|
|
313
|
-
*
|
|
314
|
-
* Returns { status, message, checkedAt, requirementLeftPending }
|
|
315
|
-
*/
|
|
316
|
-
async function checkProvider(providerId, config = {}, repoPath, onProgress = null, signal = null) {
|
|
317
|
-
const { getProviderDefinition } = require('./provider-registry');
|
|
318
|
-
const def = getProviderDefinition(providerId);
|
|
319
|
-
if (!def) {
|
|
320
|
-
return { status: 'error', message: `Unknown provider: ${providerId}`, checkedAt: new Date().toISOString(), requirementLeftPending: false };
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
if (!repoPath) {
|
|
324
|
-
return { status: 'error', message: 'No repository path available', checkedAt: new Date().toISOString(), requirementLeftPending: false };
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
const { getRequirementsPath } = require('vibecodingmachine-core');
|
|
328
|
-
let reqPath;
|
|
329
|
-
try {
|
|
330
|
-
reqPath = await getRequirementsPath(repoPath);
|
|
331
|
-
} catch {
|
|
332
|
-
reqPath = path.join(repoPath, '.vibecodingmachine', 'REQUIREMENTS.md');
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const resultFile = getResultFilePath(repoPath);
|
|
336
|
-
|
|
337
|
-
// For direct CLI providers: auto-install if missing
|
|
338
|
-
const cliInfo = CLI_AUTO_INSTALL[providerId];
|
|
339
|
-
if (cliInfo && !isCLIAvailable(cliInfo.cmd)) {
|
|
340
|
-
if (onProgress) onProgress(providerId, 'installing');
|
|
341
|
-
const { installed, note } = await installCLI(cliInfo.pkg, cliInfo.cmd);
|
|
342
|
-
if (!installed) {
|
|
343
|
-
return { status: 'error', message: `${def.name} not installed and auto-install failed: ${note}`, checkedAt: new Date().toISOString(), requirementLeftPending: false };
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// Add test requirement (idempotent — safe to call even if already present from a prior failed agent)
|
|
348
|
-
try { addTestRequirement(reqPath, resultFile); } catch (err) {
|
|
349
|
-
return { status: 'error', message: `Could not write to REQUIREMENTS.md: ${err.message}`, checkedAt: new Date().toISOString(), requirementLeftPending: false };
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const timeoutMs = def.type === 'ide' ? IDE_TIMEOUT_MS : DIRECT_TIMEOUT_MS;
|
|
353
|
-
const result = await runAgentCheck(providerId, def, repoPath, resultFile, timeoutMs, signal);
|
|
354
|
-
|
|
355
|
-
if (result.status === 'success') {
|
|
356
|
-
// Agent completed — requirement has been moved to Verified; remove it and the result file
|
|
357
|
-
try { removeTestRequirement(reqPath); } catch {}
|
|
358
|
-
try { fs.unlinkSync(resultFile); } catch {}
|
|
359
|
-
return { ...result, requirementLeftPending: false };
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// Agent failed — leave the requirement in pending for the next agent
|
|
363
|
-
return { ...result, requirementLeftPending: true };
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Check all providers sequentially.
|
|
368
|
-
* The test requirement is shared: it stays pending between agents until one
|
|
369
|
-
* succeeds, then it's removed. If all fail, it's removed at the end.
|
|
370
|
-
*
|
|
371
|
-
* Returns { [providerId]: checkResult }
|
|
372
|
-
*/
|
|
373
|
-
async function checkAllProviders(providerIds, config = {}, repoPath, onProgress = null, signal = null) {
|
|
374
|
-
const results = {};
|
|
375
|
-
|
|
376
|
-
for (const id of providerIds) {
|
|
377
|
-
if (signal && signal.cancelled) break;
|
|
378
|
-
if (onProgress) onProgress(id, 'checking');
|
|
379
|
-
results[id] = await checkProvider(id, config, repoPath, onProgress, signal);
|
|
380
|
-
if (onProgress) onProgress(id, 'done', results[id]);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// Final cleanup — remove requirement if still pending (all agents failed or skipped)
|
|
384
|
-
if (repoPath) {
|
|
385
|
-
const { getRequirementsPath } = require('vibecodingmachine-core');
|
|
386
|
-
let reqPath;
|
|
387
|
-
try { reqPath = await getRequirementsPath(repoPath); } catch {
|
|
388
|
-
reqPath = path.join(repoPath, '.vibecodingmachine', 'REQUIREMENTS.md');
|
|
389
|
-
}
|
|
390
|
-
try { removeTestRequirement(reqPath); } catch {}
|
|
391
|
-
try { fs.unlinkSync(getResultFilePath(repoPath)); } catch {}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
return results;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Format a checkedAt ISO timestamp for display.
|
|
399
|
-
* e.g. "April 12, 2025 at 4:23 pm"
|
|
400
|
-
*/
|
|
401
|
-
function formatCheckedAt(checkedAt) {
|
|
402
|
-
if (!checkedAt) return '';
|
|
403
|
-
const d = new Date(checkedAt);
|
|
404
|
-
if (isNaN(d.getTime())) return checkedAt;
|
|
405
|
-
const months = ['January','February','March','April','May','June','July','August','September','October','November','December'];
|
|
406
|
-
const timePart = d.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }).toLowerCase();
|
|
407
|
-
return `${months[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()} at ${timePart}`;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
module.exports = { checkProvider, checkAllProviders, formatCheckedAt, DIRECT_TIMEOUT_MS, IDE_TIMEOUT_MS };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawn, execSync } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* IDE information and management
|
|
9
|
+
*/
|
|
10
|
+
const IDE_INFO = {
|
|
11
|
+
'cursor': {
|
|
12
|
+
app: 'Cursor',
|
|
13
|
+
binaryName: 'Cursor',
|
|
14
|
+
processName: 'Cursor',
|
|
15
|
+
bundleId: 'com.cursor.Cursor',
|
|
16
|
+
paths: {
|
|
17
|
+
darwin: ['/Applications/Cursor.app'],
|
|
18
|
+
win32: ['C:\\Program Files\\Cursor\\Cursor.exe', 'C:\\Program Files (x86)\\Cursor\\Cursor.exe'],
|
|
19
|
+
linux: ['/usr/bin/cursor', '/usr/local/bin/cursor']
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
'windsurf': {
|
|
23
|
+
app: 'Windsurf',
|
|
24
|
+
binaryName: 'windsurf',
|
|
25
|
+
processName: 'windsurf',
|
|
26
|
+
bundleId: 'com.windsurf.Windsurf',
|
|
27
|
+
paths: {
|
|
28
|
+
darwin: ['/Applications/Windsurf.app'],
|
|
29
|
+
win32: ['C:\\Program Files\\Windsurf\\Windsurf.exe', 'C:\\Program Files (x86)\\Windsurf\\Windsurf.exe'],
|
|
30
|
+
linux: ['/usr/bin/windsurf', '/usr/local/bin/windsurf']
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
'vscode': {
|
|
34
|
+
app: 'VS Code',
|
|
35
|
+
binaryName: 'code',
|
|
36
|
+
processName: 'Code',
|
|
37
|
+
bundleId: 'com.microsoft.VSCode',
|
|
38
|
+
paths: {
|
|
39
|
+
darwin: ['/Applications/Visual Studio Code.app'],
|
|
40
|
+
win32: ['C:\\Program Files\\Microsoft VS Code\\Code.exe', 'C:\\Program Files (x86)\\Microsoft VS Code\\Code.exe'],
|
|
41
|
+
linux: ['/usr/bin/code', '/usr/local/bin/code']
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check if IDE is installed
|
|
48
|
+
*/
|
|
49
|
+
function isIdeInstalled(providerId) {
|
|
50
|
+
const info = IDE_INFO[providerId];
|
|
51
|
+
if (!info) return false;
|
|
52
|
+
|
|
53
|
+
const platform = process.platform;
|
|
54
|
+
const paths = info.paths[platform] || [];
|
|
55
|
+
|
|
56
|
+
return paths.some(p => {
|
|
57
|
+
try {
|
|
58
|
+
return fs.existsSync(p);
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get running IDE process
|
|
67
|
+
*/
|
|
68
|
+
function getIdeProcess(providerId) {
|
|
69
|
+
const info = IDE_INFO[providerId];
|
|
70
|
+
if (!info) return null;
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const cmd = process.platform === 'win32'
|
|
74
|
+
? `tasklist /FI "IMAGENAME eq ${info.binaryName}.exe"`
|
|
75
|
+
: `ps aux | grep -i "${info.processName}" | grep -v grep`;
|
|
76
|
+
|
|
77
|
+
const result = execSync(cmd, { encoding: 'utf8' });
|
|
78
|
+
return result.trim().length > 0 ? result.trim() : null;
|
|
79
|
+
} catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Launch IDE if not running
|
|
86
|
+
*/
|
|
87
|
+
async function launchIde(providerId) {
|
|
88
|
+
const info = IDE_INFO[providerId];
|
|
89
|
+
if (!info) return false;
|
|
90
|
+
|
|
91
|
+
const platform = process.platform;
|
|
92
|
+
const paths = info.paths[platform] || [];
|
|
93
|
+
const idePath = paths.find(p => fs.existsSync(p));
|
|
94
|
+
|
|
95
|
+
if (!idePath) return false;
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
if (platform === 'darwin') {
|
|
99
|
+
// macOS: use open command
|
|
100
|
+
spawn('open', ['-a', info.app], {
|
|
101
|
+
stdio: 'ignore',
|
|
102
|
+
detached: true
|
|
103
|
+
}).unref();
|
|
104
|
+
} else if (platform === 'win32') {
|
|
105
|
+
// Windows: spawn the executable
|
|
106
|
+
spawn(idePath, [], {
|
|
107
|
+
stdio: 'ignore',
|
|
108
|
+
detached: true
|
|
109
|
+
}).unref();
|
|
110
|
+
} else {
|
|
111
|
+
// Linux: spawn the binary
|
|
112
|
+
spawn(info.binaryName, [], {
|
|
113
|
+
stdio: 'ignore',
|
|
114
|
+
detached: true
|
|
115
|
+
}).unref();
|
|
116
|
+
}
|
|
117
|
+
return true;
|
|
118
|
+
} catch {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = {
|
|
124
|
+
IDE_INFO,
|
|
125
|
+
isIdeInstalled,
|
|
126
|
+
getIdeProcess,
|
|
127
|
+
launchIde
|
|
128
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get the Node.js executable path
|
|
9
|
+
* When running in Electron, process.execPath points to electron executable, not node
|
|
10
|
+
*/
|
|
11
|
+
function getNodeExecutable() {
|
|
12
|
+
// First try to get the actual node executable from process.env
|
|
13
|
+
if (process.env.NODE_BINARY_PATH) {
|
|
14
|
+
return process.env.NODE_BINARY_PATH;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Try to find node in PATH
|
|
18
|
+
try {
|
|
19
|
+
const nodeCmd = process.platform === 'win32' ? 'where node' : 'which node';
|
|
20
|
+
const nodePath = execSync(nodeCmd, { encoding: 'utf8' }).trim().split('\n')[0];
|
|
21
|
+
if (nodePath && fs.existsSync(nodePath)) {
|
|
22
|
+
return nodePath;
|
|
23
|
+
}
|
|
24
|
+
} catch (_) {}
|
|
25
|
+
|
|
26
|
+
// Fallback: try common node installation paths
|
|
27
|
+
const commonPaths = process.platform === 'win32'
|
|
28
|
+
? [
|
|
29
|
+
'C:\\Program Files\\nodejs\\node.exe',
|
|
30
|
+
'C:\\Program Files (x86)\\nodejs\\node.exe',
|
|
31
|
+
path.join(process.env.PROGRAMFILES || '', 'nodejs', 'node.exe'),
|
|
32
|
+
path.join(process.env['PROGRAMFILES(X86)'] || '', 'nodejs', 'node.exe')
|
|
33
|
+
]
|
|
34
|
+
: [
|
|
35
|
+
'/usr/bin/node',
|
|
36
|
+
'/usr/local/bin/node',
|
|
37
|
+
'/opt/homebrew/bin/node'
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
for (const nodePath of commonPaths) {
|
|
41
|
+
if (nodePath && fs.existsSync(nodePath)) {
|
|
42
|
+
return nodePath;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Last resort: use process.execPath (might be electron but worth trying)
|
|
47
|
+
console.warn('[AGENT CHECK] Could not find node executable, falling back to process.execPath');
|
|
48
|
+
return process.execPath;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = { getNodeExecutable };
|