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.
Files changed (101) hide show
  1. package/bin/auth/auth-compliance.js +126 -0
  2. package/bin/cli-program.js +104 -0
  3. package/bin/cli-setup.js +52 -0
  4. package/bin/commands/agent-commands.js +310 -0
  5. package/bin/commands/auto-commands.js +70 -0
  6. package/bin/commands/command-aliases.js +118 -0
  7. package/bin/commands/repo-commands.js +39 -0
  8. package/bin/commands/rui-commands.js +152 -0
  9. package/bin/config/cli-config.js +394 -0
  10. package/bin/init/environment-setup.js +84 -0
  11. package/bin/update/update-checker.js +126 -0
  12. package/bin/vibecodingmachine-new.js +50 -0
  13. package/bin/vibecodingmachine.js +29 -663
  14. package/package.json +8 -2
  15. package/src/commands/agents/add.js +277 -0
  16. package/src/commands/agents/check.js +380 -0
  17. package/src/commands/agents/list.js +471 -0
  18. package/src/commands/agents/remove.js +351 -0
  19. package/src/commands/analyze-file-sizes.js +428 -0
  20. package/src/commands/auto-direct/code-processor.js +282 -0
  21. package/src/commands/auto-direct/file-scanner.js +266 -0
  22. package/src/commands/auto-direct/provider-config.js +178 -0
  23. package/src/commands/auto-direct/provider-manager.js +219 -0
  24. package/src/commands/auto-direct/requirement-manager.js +172 -0
  25. package/src/commands/auto-direct/status-display.js +91 -0
  26. package/src/commands/auto-direct/utils.js +106 -0
  27. package/src/commands/auto-direct.js +875 -488
  28. package/src/commands/auto-execution.js +342 -0
  29. package/src/commands/auto-provider-management.js +102 -0
  30. package/src/commands/auto-requirement-management.js +161 -0
  31. package/src/commands/auto-status-helpers.js +141 -0
  32. package/src/commands/auto.js +105 -5155
  33. package/src/commands/check-compliance.js +536 -0
  34. package/src/commands/continuous-scan.js +119 -0
  35. package/src/commands/ide.js +16 -4
  36. package/src/commands/refactor-file.js +486 -0
  37. package/src/commands/requirements.js +301 -2
  38. package/src/commands/timeout.js +290 -0
  39. package/src/trui/TruiInterface.js +108 -0
  40. package/src/trui/agents/AgentInterface.js +580 -0
  41. package/src/utils/antigravity-installer.js +60 -6
  42. package/src/utils/clarification-actions.js +290 -0
  43. package/src/utils/config.js +123 -2
  44. package/src/utils/first-run.js +5 -5
  45. package/src/utils/ide-handlers.js +212 -0
  46. package/src/utils/interactive/clarification-actions.js +348 -0
  47. package/src/utils/interactive/core-ui.js +265 -0
  48. package/src/utils/interactive/file-backup.js +237 -0
  49. package/src/utils/interactive/file-import-export.js +305 -0
  50. package/src/utils/interactive/file-operations.js +49 -0
  51. package/src/utils/interactive/file-validation.js +276 -0
  52. package/src/utils/interactive/interactive-prompts.js +480 -0
  53. package/src/utils/interactive/requirement-actions.js +127 -0
  54. package/src/utils/interactive/requirement-crud.js +356 -0
  55. package/src/utils/interactive/requirements-navigation.js +286 -0
  56. package/src/utils/interactive.js +390 -3459
  57. package/src/utils/provider-checker/agent-checker.js +250 -0
  58. package/src/utils/provider-checker/agent-runner.js +450 -0
  59. package/src/utils/provider-checker/cli-installer.js +123 -0
  60. package/src/utils/provider-checker/cli-utils.js +15 -0
  61. package/src/utils/provider-checker/format-utils.js +32 -0
  62. package/src/utils/provider-checker/ide-manager.js +72 -0
  63. package/src/utils/provider-checker/ide-utils.js +71 -0
  64. package/src/utils/provider-checker/node-detector.js +56 -0
  65. package/src/utils/provider-checker/node-utils.js +61 -0
  66. package/src/utils/provider-checker/process-spawn.js +22 -0
  67. package/src/utils/provider-checker/process-utils.js +37 -0
  68. package/src/utils/provider-checker/provider-validator.js +160 -0
  69. package/src/utils/provider-checker/quota-checker.js +54 -0
  70. package/src/utils/provider-checker/quota-detector.js +44 -0
  71. package/src/utils/provider-checker/requirements-manager.js +94 -0
  72. package/src/utils/provider-checker/test-requirements.js +95 -0
  73. package/src/utils/provider-checker/time-formatter.js +18 -0
  74. package/src/utils/provider-checker-new.js +14 -0
  75. package/src/utils/provider-checker.js +12 -407
  76. package/src/utils/provider-checkers/ide-manager.js +128 -0
  77. package/src/utils/provider-checkers/node-executable-finder.js +51 -0
  78. package/src/utils/provider-checkers/provider-checker-core.js +172 -0
  79. package/src/utils/provider-checkers/provider-checker-main.js +107 -0
  80. package/src/utils/provider-manager.js +60 -4
  81. package/src/utils/provider-registry.js +26 -3
  82. package/src/utils/provider-utils.js +173 -0
  83. package/src/utils/quota-detectors.js +212 -0
  84. package/src/utils/requirement-action-handlers.js +288 -0
  85. package/src/utils/requirement-actions/clarification-actions.js +229 -0
  86. package/src/utils/requirement-actions/confirmation-prompts.js +93 -0
  87. package/src/utils/requirement-actions/file-operations.js +92 -0
  88. package/src/utils/requirement-actions/helpers.js +40 -0
  89. package/src/utils/requirement-actions/requirement-operations.js +335 -0
  90. package/src/utils/requirement-actions.js +46 -856
  91. package/src/utils/requirement-file-operations.js +259 -0
  92. package/src/utils/requirement-helpers.js +128 -0
  93. package/src/utils/requirement-management.js +279 -0
  94. package/src/utils/requirement-navigation.js +146 -0
  95. package/src/utils/requirement-organization.js +271 -0
  96. package/src/utils/simple-trui.js +75 -1
  97. package/src/utils/trui-navigation.js +28 -2
  98. package/src/utils/trui-req-tree.js +196 -11
  99. package/src/utils/trui-specifications.js +31 -1
  100. package/src/utils/interactive-backup.js +0 -5664
  101. package/src/utils/trui-provider-manager.js +0 -182
@@ -0,0 +1,172 @@
1
+ 'use strict';
2
+
3
+ const { spawn } = require('child_process');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const chokidar = require('chokidar');
7
+ const { getNodeExecutable } = require('./node-executable-finder');
8
+ const { isIdeInstalled, getIdeProcess, launchIde, IDE_INFO } = require('./ide-manager');
9
+
10
+ const CLI_ENTRY_POINT = path.resolve(path.join(__dirname, '../../../bin/vibecodingmachine.js'));
11
+
12
+ // Timeout constants
13
+ const DIRECT_TIMEOUT_MS = 60000; // 1 minute for direct providers
14
+ const IDE_TIMEOUT_MS = 300000; // 5 minutes for IDE providers
15
+
16
+ /**
17
+ * Get result file path for provider check
18
+ */
19
+ function getResultFilePath(repoPath) {
20
+ return path.join(repoPath, '.vibecodingmachine', 'agent-check-result.txt');
21
+ }
22
+
23
+ /**
24
+ * Remove test requirement file
25
+ */
26
+ function removeTestRequirement(reqPath) {
27
+ if (fs.existsSync(reqPath)) {
28
+ let content = fs.readFileSync(reqPath, 'utf8');
29
+ // Remove the test requirement if it exists
30
+ content = content.replace(/^## 🚦 Current Status\n\nAGENT_CHECK[^\n]*\n\n?/gm, '');
31
+ fs.writeFileSync(reqPath, content);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Check if provider is reachable
37
+ */
38
+ async function checkProviderReachable(providerId, config, repoPath) {
39
+ const def = config.providers[providerId];
40
+ if (!def) {
41
+ return { status: 'error', message: `Provider ${providerId} not found in config` };
42
+ }
43
+
44
+ const checkedAt = new Date().toISOString();
45
+ const resultFile = getResultFilePath(repoPath);
46
+
47
+ // For IDE providers, check if IDE is installed and running
48
+ if (def.type === 'ide') {
49
+ if (!isIdeInstalled(providerId)) {
50
+ return { status: 'error', message: `${IDE_INFO[providerId]?.app || providerId} is not installed`, checkedAt };
51
+ }
52
+
53
+ const processInfo = getIdeProcess(providerId);
54
+ if (!processInfo) {
55
+ // Try to launch the IDE
56
+ const launched = await launchIde(providerId);
57
+ if (!launched) {
58
+ return { status: 'error', message: `${IDE_INFO[providerId]?.app || providerId} is not running and could not be launched`, checkedAt };
59
+ }
60
+ // Give IDE time to start
61
+ await new Promise(r => setTimeout(r, 5000));
62
+ }
63
+ }
64
+
65
+ return new Promise((resolve) => {
66
+ let resolved = false;
67
+ let child = null;
68
+
69
+ async function done(result) {
70
+ if (resolved) return;
71
+ resolved = true;
72
+ try {
73
+ if (watcher) watcher.close();
74
+ } catch {}
75
+ clearTimeout(timeout);
76
+ clearInterval(cancelCheckInterval);
77
+ try {
78
+ if (child) child.kill();
79
+ } catch {}
80
+ resolve(result);
81
+ }
82
+
83
+ function checkResult() {
84
+ if (!resolved && fs.existsSync(resultFile)) {
85
+ try {
86
+ const content = fs.readFileSync(resultFile, 'utf8');
87
+ if (content.includes('VCM_CHECK_OK')) {
88
+ done({ status: 'success', message: 'End-to-end check passed', checkedAt });
89
+ }
90
+ } catch {}
91
+ }
92
+ }
93
+
94
+ const watcher = chokidar.watch(resultFile, { persistent: true, ignoreInitial: false, disableGlobbing: true });
95
+ watcher.on('add', checkResult);
96
+ watcher.on('change', checkResult);
97
+
98
+ const timeout = setTimeout(() => {
99
+ let diagnosis = '';
100
+ if (def.type === 'ide') {
101
+ const info = IDE_INFO[providerId] || IDE_INFO[def.ide] || null;
102
+ const processInfo = getIdeProcess(providerId);
103
+ diagnosis = processInfo ? 'IDE is running but check timed out' : 'IDE process not found';
104
+ } else {
105
+ diagnosis = 'Provider check timed out';
106
+ }
107
+ done({ status: 'timeout', message: `Check timed out (${def.type === 'ide' ? '5min' : '1min'}). ${diagnosis}`, checkedAt });
108
+ }, def.type === 'ide' ? IDE_TIMEOUT_MS : DIRECT_TIMEOUT_MS);
109
+
110
+ const cancelCheckInterval = setInterval(() => {
111
+ if (resolved) return;
112
+ // Additional periodic checks can be added here
113
+ }, 10000);
114
+
115
+ // Prepare environment
116
+ const env = { ...process.env };
117
+ env.NODE_BINARY_PATH = getNodeExecutable();
118
+ if (repoPath) {
119
+ env.VCM_CHECK_REPO = repoPath;
120
+ }
121
+
122
+ // Run the check
123
+ try {
124
+ const nodePath = getNodeExecutable();
125
+ const args = [CLI_ENTRY_POINT, 'check-provider', providerId];
126
+
127
+ child = spawn(nodePath, args, {
128
+ stdio: 'ignore',
129
+ env,
130
+ cwd: repoPath || process.cwd(),
131
+ detached: true
132
+ });
133
+
134
+ child.on('error', (error) => {
135
+ done({ status: 'error', message: `Failed to start check: ${error.message}`, checkedAt });
136
+ });
137
+
138
+ child.on('exit', (code, signal) => {
139
+ if (!resolved) {
140
+ if (signal === 'SIGTERM' || signal === 'SIGKILL') {
141
+ // Expected termination
142
+ return;
143
+ }
144
+ if (code === 0) {
145
+ // Success, but we should have gotten the result via file
146
+ setTimeout(() => {
147
+ if (!resolved) {
148
+ checkResult();
149
+ if (!resolved) {
150
+ done({ status: 'partial', message: 'Check completed but no result file found', checkedAt });
151
+ }
152
+ }
153
+ }, 2000);
154
+ } else {
155
+ done({ status: 'error', message: `Check failed with exit code ${code}`, checkedAt });
156
+ }
157
+ }
158
+ });
159
+
160
+ } catch (error) {
161
+ done({ status: 'error', message: `Failed to execute check: ${error.message}`, checkedAt });
162
+ }
163
+ });
164
+ }
165
+
166
+ module.exports = {
167
+ checkProviderReachable,
168
+ getResultFilePath,
169
+ removeTestRequirement,
170
+ DIRECT_TIMEOUT_MS,
171
+ IDE_TIMEOUT_MS
172
+ };
@@ -0,0 +1,107 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { checkProviderReachable, getResultFilePath, removeTestRequirement } = require('./provider-checker-core');
6
+
7
+ /**
8
+ * Check a single provider
9
+ */
10
+ async function checkProvider(providerId, config, repoPath, onProgress, signal) {
11
+ if (signal?.aborted) {
12
+ return { status: 'skipped', message: 'Check aborted' };
13
+ }
14
+
15
+ try {
16
+ // Ensure .vibecodingmachine directory exists
17
+ if (repoPath) {
18
+ const vcmDir = path.join(repoPath, '.vibecodingmachine');
19
+ if (!fs.existsSync(vcmDir)) {
20
+ fs.mkdirSync(vcmDir, { recursive: true });
21
+ }
22
+ }
23
+
24
+ // Add test requirement
25
+ if (repoPath) {
26
+ const { getRequirementsPath } = require('vibecodingmachine-core');
27
+ let reqPath;
28
+ try {
29
+ reqPath = await getRequirementsPath(repoPath);
30
+ } catch {
31
+ reqPath = path.join(repoPath, '.vibecodingmachine', 'REQUIREMENTS.md');
32
+ }
33
+
34
+ if (fs.existsSync(reqPath)) {
35
+ const content = fs.readFileSync(reqPath, 'utf8');
36
+ if (!content.includes('AGENT_CHECK')) {
37
+ fs.appendFileSync(reqPath, '\n## 🚦 Current Status\n\nAGENT_CHECK - Provider connectivity check\n\n');
38
+ }
39
+ }
40
+ }
41
+
42
+ // Perform the check
43
+ const result = await checkProviderReachable(providerId, config, repoPath);
44
+ return result;
45
+
46
+ } catch (error) {
47
+ return {
48
+ status: 'error',
49
+ message: `Check failed: ${error.message}`,
50
+ error: error.stack
51
+ };
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Check all providers
57
+ */
58
+ async function checkAllProviders(config, repoPath, onProgress, signal) {
59
+ const results = {};
60
+ const providerIds = Object.keys(config.providers || {});
61
+
62
+ for (const id of providerIds) {
63
+ if (signal?.aborted) break;
64
+
65
+ if (onProgress) onProgress(id, 'starting');
66
+ results[id] = await checkProvider(id, config, repoPath, onProgress, signal);
67
+ if (onProgress) onProgress(id, 'done', results[id]);
68
+ }
69
+
70
+ // Final cleanup — remove requirement if still pending (all agents failed or skipped)
71
+ if (repoPath) {
72
+ const { getRequirementsPath } = require('vibecodingmachine-core');
73
+ let reqPath;
74
+ try {
75
+ reqPath = await getRequirementsPath(repoPath);
76
+ } catch {
77
+ reqPath = path.join(repoPath, '.vibecodingmachine', 'REQUIREMENTS.md');
78
+ }
79
+ try {
80
+ removeTestRequirement(reqPath);
81
+ } catch {}
82
+ try {
83
+ fs.unlinkSync(getResultFilePath(repoPath));
84
+ } catch {}
85
+ }
86
+
87
+ return results;
88
+ }
89
+
90
+ /**
91
+ * Format a checkedAt ISO timestamp for display.
92
+ * e.g. "April 12, 2025 at 4:23 pm"
93
+ */
94
+ function formatCheckedAt(checkedAt) {
95
+ if (!checkedAt) return '';
96
+ const d = new Date(checkedAt);
97
+ if (isNaN(d.getTime())) return checkedAt;
98
+ const months = ['January','February','March','April','May','June','July','August','September','October','November','December'];
99
+ const timePart = d.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }).toLowerCase();
100
+ return `${months[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()} at ${timePart}`;
101
+ }
102
+
103
+ module.exports = {
104
+ checkProvider,
105
+ checkAllProviders,
106
+ formatCheckedAt
107
+ };
@@ -1,7 +1,7 @@
1
1
  const chalk = require('chalk');
2
2
  const readline = require('readline');
3
3
  const { t } = require('vibecodingmachine-core');
4
- const { getProviderDefinitions, getProviderPreferences, saveProviderPreferences, getProviderCache, setProviderCache } = require('../utils/provider-registry');
4
+ const { getProviderDefinitions, getProviderPreferences, saveProviderPreferences, getProviderCache, setProviderCache, getDefaultProviderOrder } = require('../utils/provider-registry');
5
5
  const { checkVSCodeExtension, checkAppOrBinary } = require('./ide-detection');
6
6
  const { formatIDEName, getAgentDisplayName } = require('./display-formatters');
7
7
  const { IDEHealthTracker } = require('vibecodingmachine-core');
@@ -19,7 +19,7 @@ async function showProviderManagerMenu() {
19
19
  const definitions = getProviderDefinitions();
20
20
  const defMap = new Map(definitions.map(def => [def.id, def]));
21
21
  const prefs = await getProviderPreferences();
22
- let order = prefs.order.slice();
22
+ let order = Array.isArray(prefs.order) ? prefs.order.slice() : getDefaultProviderOrder();
23
23
  let enabled = { ...prefs.enabled };
24
24
  let selectedIndex = 0;
25
25
  let dirty = false;
@@ -104,7 +104,7 @@ async function showProviderManagerMenu() {
104
104
  console.log(chalk.bold.cyan('⚙ ' + t('provider.title') + '\n'));
105
105
 
106
106
  // Header
107
- console.log(chalk.gray('Use ↑↓ to move, Space to toggle, → to configure, X/ESC to exit\n'));
107
+ console.log(chalk.gray('Use ↑↓ to move, Space to toggle, </> to reorder (< down, > up), → to configure, !/1 to check agents, X/ESC to exit\n'));
108
108
 
109
109
  // Display providers
110
110
  order.forEach((id, index) => {
@@ -123,6 +123,17 @@ async function showProviderManagerMenu() {
123
123
  statusIcon = isEnabled ? '🟢' : '🔴';
124
124
  statusColor = isEnabled ? chalk.green : chalk.red;
125
125
  statusText = isEnabled ? 'Enabled' : 'Disabled';
126
+ } else {
127
+ // Show enabled/disabled state even for uninstalled agents
128
+ if (isEnabled) {
129
+ statusIcon = '🟡';
130
+ statusColor = chalk.yellow;
131
+ statusText = 'Enabled (not installed)';
132
+ } else {
133
+ statusIcon = '⚪';
134
+ statusColor = chalk.gray;
135
+ statusText = 'Disabled (not installed)';
136
+ }
126
137
  }
127
138
 
128
139
  const prefix = isSelected ? chalk.cyan('❯ ') : ' ';
@@ -174,6 +185,27 @@ async function showProviderManagerMenu() {
174
185
  }
175
186
 
176
187
  switch (key.name) {
188
+ case '!':
189
+ case '1':
190
+ // Run agents check command
191
+ isMenuActive = false;
192
+ if (dirty) {
193
+ await saveProviderPreferences({ order, enabled });
194
+ await setProviderCache(Object.fromEntries(installationStatus));
195
+ }
196
+ // Trigger agents check after returning
197
+ setTimeout(() => {
198
+ console.clear();
199
+ console.log(chalk.bold.cyan('🔍 Checking agents...\n'));
200
+ require('../commands/agents-check')().then(() => {
201
+ process.exit(0);
202
+ }).catch((err) => {
203
+ console.log(chalk.red('Agents check error: ' + err.message));
204
+ process.exit(1);
205
+ });
206
+ }, 100);
207
+ return;
208
+
177
209
  case 'escape':
178
210
  case 'x':
179
211
  isMenuActive = false;
@@ -191,6 +223,30 @@ async function showProviderManagerMenu() {
191
223
  selectedIndex = Math.min(order.length - 1, selectedIndex + 1);
192
224
  break;
193
225
 
226
+ case 'left':
227
+ case '<':
228
+ // Move selected provider down in order
229
+ if (selectedIndex < order.length - 1) {
230
+ const temp = order[selectedIndex];
231
+ order[selectedIndex] = order[selectedIndex + 1];
232
+ order[selectedIndex + 1] = temp;
233
+ selectedIndex++;
234
+ dirty = true;
235
+ }
236
+ break;
237
+
238
+ case 'right':
239
+ case '>':
240
+ // Move selected provider up in order
241
+ if (selectedIndex > 0) {
242
+ const temp = order[selectedIndex];
243
+ order[selectedIndex] = order[selectedIndex - 1];
244
+ order[selectedIndex - 1] = temp;
245
+ selectedIndex--;
246
+ dirty = true;
247
+ }
248
+ break;
249
+
194
250
  case 'space':
195
251
  case 'return':
196
252
  const currentId = order[selectedIndex];
@@ -198,7 +254,7 @@ async function showProviderManagerMenu() {
198
254
  dirty = true;
199
255
  break;
200
256
 
201
- case 'right':
257
+ case 'c':
202
258
  // Configure selected provider
203
259
  const selectedId = order[selectedIndex];
204
260
  const def = defMap.get(selectedId);
@@ -110,6 +110,14 @@ const PROVIDER_DEFINITIONS = [
110
110
  defaultModel: 'cline-cli',
111
111
  estimatedSpeed: 55000
112
112
  },
113
+ {
114
+ id: 'opencode',
115
+ name: 'OpenCode CLI',
116
+ type: 'direct',
117
+ category: 'llm',
118
+ defaultModel: 'opencode-cli',
119
+ estimatedSpeed: 40000
120
+ },
113
121
  {
114
122
  id: 'replit',
115
123
  name: 'Replit Agent',
@@ -157,6 +165,7 @@ function getProviderDisplayName(id) {
157
165
  'vscode': 'ide.agent.vscode',
158
166
  'cline': 'ide.agent.cline',
159
167
  'claude-code': 'ide.agent.claude.code',
168
+ 'opencode': 'ide.agent.opencode',
160
169
  'antigravity': 'ide.agent.antigravity',
161
170
  'github-copilot': 'ide.agent.github.copilot',
162
171
  'amazon-q': 'ide.agent.amazon.q',
@@ -185,12 +194,13 @@ function mergeProviderPreferences(autoConfig) {
185
194
 
186
195
  const defaultOrder = getDefaultProviderOrder();
187
196
  const cleanedOrder = order.filter(id => getProviderDefinition(id));
188
- defaultOrder.forEach(id => {
197
+ defaultOrder.forEach((id, index) => {
189
198
  if (!cleanedOrder.includes(id)) {
190
199
  cleanedOrder.push(id);
191
200
  }
192
201
  if (enabled[id] === undefined) {
193
- enabled[id] = false;
202
+ // Enable the first provider by default (antigravity)
203
+ enabled[id] = index === 0;
194
204
  }
195
205
  });
196
206
 
@@ -205,12 +215,23 @@ async function getProviderPreferences() {
205
215
  async function saveProviderPreferences(order, enabled) {
206
216
  await setAutoConfig({
207
217
  providerPreferences: {
208
- order: order.slice(),
218
+ order: Array.isArray(order) ? order.slice() : [],
209
219
  enabled: { ...enabled }
210
220
  }
211
221
  });
212
222
  }
213
223
 
224
+ async function getProviderCache() {
225
+ const autoConfig = await getAutoConfig();
226
+ return autoConfig.providerCache || {};
227
+ }
228
+
229
+ async function setProviderCache(cache) {
230
+ await setAutoConfig({
231
+ providerCache: { ...cache }
232
+ });
233
+ }
234
+
214
235
  module.exports = {
215
236
  getProviderDefinitions,
216
237
  getProviderDefinition,
@@ -219,6 +240,8 @@ module.exports = {
219
240
  mergeProviderPreferences,
220
241
  getProviderPreferences,
221
242
  saveProviderPreferences,
243
+ getProviderCache,
244
+ setProviderCache,
222
245
  PROVIDER_DEFINITIONS
223
246
  };
224
247
 
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Provider Utils Module
3
+ *
4
+ * Contains utility functions for provider checking.
5
+ */
6
+
7
+ const { spawn, execSync } = require('child_process');
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ const CLI_ENTRY_POINT = path.resolve(path.join(__dirname, '../../bin/vibecodingmachine.js'));
12
+
13
+ /**
14
+ * Get the Node.js executable path
15
+ * When running in Electron, process.execPath points to electron executable, not node
16
+ */
17
+ function getNodeExecutable() {
18
+ // First try to get the actual node executable from process.env
19
+ if (process.env.NODE_BINARY_PATH) {
20
+ return process.env.NODE_BINARY_PATH;
21
+ }
22
+
23
+ // Try to find node in PATH
24
+ try {
25
+ const nodeCmd = process.platform === 'win32' ? 'where node' : 'which node';
26
+ const nodePath = execSync(nodeCmd, { encoding: 'utf8' }).trim().split('\n')[0];
27
+ if (nodePath && fs.existsSync(nodePath)) {
28
+ return nodePath;
29
+ }
30
+ } catch (_) {}
31
+
32
+ // Fallback: try common node installation paths
33
+ const commonPaths = process.platform === 'win32'
34
+ ? [
35
+ 'C:\\Program Files\\nodejs\\node.exe',
36
+ 'C:\\Program Files (x86)\\nodejs\\node.exe',
37
+ path.join(process.env.PROGRAMFILES || '', 'nodejs', 'node.exe'),
38
+ path.join(process.env['PROGRAMFILES(X86)'] || '', 'nodejs', 'node.exe')
39
+ ]
40
+ : [
41
+ '/usr/bin/node',
42
+ '/usr/local/bin/node',
43
+ '/opt/homebrew/bin/node'
44
+ ];
45
+
46
+ for (const nodePath of commonPaths) {
47
+ if (nodePath && fs.existsSync(nodePath)) {
48
+ return nodePath;
49
+ }
50
+ }
51
+
52
+ // Last resort: use process.execPath (might be electron but worth trying)
53
+ console.warn('[AGENT CHECK] Could not find node executable, falling back to process.execPath');
54
+ return process.execPath;
55
+ }
56
+
57
+ /**
58
+ * Get result file path for provider check
59
+ */
60
+ function getResultFilePath(repoPath) {
61
+ return path.join(repoPath, '.vibecodingmachine', 'agent-check-results.json');
62
+ }
63
+
64
+ /**
65
+ * Remove test requirement from requirements file
66
+ */
67
+ function removeTestRequirement(reqPath) {
68
+ if (!fs.existsSync(reqPath)) return;
69
+
70
+ const content = fs.readFileSync(reqPath, 'utf8');
71
+ const lines = content.split('\n');
72
+
73
+ // Find and remove the test requirement
74
+ const startIdx = lines.findIndex(line => line.includes('### AGENT CHECK TEST'));
75
+ if (startIdx !== -1) {
76
+ let endIdx = startIdx + 1;
77
+ while (endIdx < lines.length && !lines[endIdx].startsWith('###') && !lines[endIdx].startsWith('##')) {
78
+ endIdx++;
79
+ }
80
+ lines.splice(startIdx, endIdx - startIdx);
81
+ fs.writeFileSync(reqPath, lines.join('\n'));
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Format a checkedAt ISO timestamp for display.
87
+ * e.g. "April 12, 2025 at 4:23 pm"
88
+ */
89
+ function formatCheckedAt(checkedAt) {
90
+ if (!checkedAt) return '';
91
+ const d = new Date(checkedAt);
92
+ if (isNaN(d.getTime())) return checkedAt;
93
+ const months = ['January','February','March','April','May','June','July','August','September','October','November','December'];
94
+ const timePart = d.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }).toLowerCase();
95
+ return `${months[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()} at ${timePart}`;
96
+ }
97
+
98
+ /**
99
+ * Create a test requirement for checking
100
+ */
101
+ function createTestRequirement(reqPath) {
102
+ const testReq = `### AGENT CHECK TEST
103
+
104
+ **Status:** NOT WORKING
105
+ **Response from last chat:** Testing provider connectivity...
106
+
107
+ ---
108
+
109
+ This is a temporary test requirement used to verify agent providers are working.
110
+ It will be automatically removed after the check completes.
111
+ `;
112
+
113
+ try {
114
+ // Ensure directory exists
115
+ fs.mkdirSync(path.dirname(reqPath), { recursive: true });
116
+
117
+ // Read existing content
118
+ let content = '';
119
+ if (fs.existsSync(reqPath)) {
120
+ content = fs.readFileSync(reqPath, 'utf8');
121
+ } else {
122
+ content = '# Requirements\n\n';
123
+ }
124
+
125
+ // Add test requirement
126
+ if (!content.includes('### AGENT CHECK TEST')) {
127
+ content += '\n' + testReq;
128
+ fs.writeFileSync(reqPath, content);
129
+ }
130
+ } catch (error) {
131
+ console.warn('[AGENT CHECK] Could not create test requirement:', error.message);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Parse provider check result from file
137
+ */
138
+ function parseResultFile(resultFile) {
139
+ try {
140
+ if (!fs.existsSync(resultFile)) {
141
+ return null;
142
+ }
143
+
144
+ const content = fs.readFileSync(resultFile, 'utf8');
145
+ const lines = content.trim().split('\n');
146
+
147
+ // Find the result line (should be the last non-empty line)
148
+ for (let i = lines.length - 1; i >= 0; i--) {
149
+ const line = lines[i].trim();
150
+ if (line && !line.startsWith('[') && !line.startsWith('Agent check')) {
151
+ try {
152
+ return JSON.parse(line);
153
+ } catch {
154
+ // Not JSON, continue searching
155
+ }
156
+ }
157
+ }
158
+ } catch (error) {
159
+ console.warn('[AGENT CHECK] Error parsing result file:', error.message);
160
+ }
161
+
162
+ return null;
163
+ }
164
+
165
+ module.exports = {
166
+ getNodeExecutable,
167
+ getResultFilePath,
168
+ removeTestRequirement,
169
+ formatCheckedAt,
170
+ createTestRequirement,
171
+ parseResultFile,
172
+ CLI_ENTRY_POINT
173
+ };