vibecodingmachine-cli 2026.2.26-1752 → 2026.3.9-1621
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 +7 -1
- package/bin/commands/agent-commands.js +150 -228
- package/bin/commands/command-aliases.js +68 -0
- package/bin/vibecodingmachine.js +1 -2
- package/package.json +2 -2
- package/src/commands/agents/list.js +71 -115
- package/src/commands/agents-check.js +16 -4
- package/src/commands/analyze-file-sizes.js +1 -1
- package/src/commands/auto-direct/auto-provider-manager.js +290 -0
- package/src/commands/auto-direct/auto-status-display.js +331 -0
- package/src/commands/auto-direct/auto-utils.js +439 -0
- package/src/commands/auto-direct/file-operations.js +110 -0
- package/src/commands/auto-direct/provider-config.js +1 -1
- package/src/commands/auto-direct/provider-manager.js +1 -1
- package/src/commands/auto-direct/status-display.js +1 -1
- package/src/commands/auto-direct/utils.js +24 -18
- package/src/commands/auto-direct-refactored.js +413 -0
- package/src/commands/auto-direct.js +594 -188
- package/src/commands/requirements/commands.js +353 -0
- package/src/commands/requirements/default-handlers.js +272 -0
- package/src/commands/requirements/disable.js +97 -0
- package/src/commands/requirements/enable.js +97 -0
- package/src/commands/requirements/utils.js +194 -0
- package/src/commands/requirements-refactored.js +60 -0
- package/src/commands/requirements.js +38 -771
- package/src/commands/specs/disable.js +96 -0
- package/src/commands/specs/enable.js +96 -0
- package/src/trui/TruiInterface.js +5 -11
- package/src/trui/agents/AgentInterface.js +24 -396
- package/src/trui/agents/handlers/CommandHandler.js +93 -0
- package/src/trui/agents/handlers/ContextManager.js +117 -0
- package/src/trui/agents/handlers/DisplayHandler.js +243 -0
- package/src/trui/agents/handlers/HelpHandler.js +51 -0
- package/src/utils/auth.js +13 -111
- package/src/utils/config.js +4 -0
- package/src/utils/interactive/requirements-navigation.js +17 -15
- package/src/utils/interactive-broken.js +2 -2
- package/src/utils/provider-checker/agent-runner.js +15 -1
- package/src/utils/provider-checker/cli-installer.js +149 -7
- package/src/utils/provider-checker/opencode-checker.js +588 -0
- package/src/utils/provider-checker/provider-validator.js +88 -3
- package/src/utils/provider-checker/time-formatter.js +3 -2
- package/src/utils/provider-manager.js +28 -20
- package/src/utils/provider-registry.js +35 -3
- package/src/utils/requirements-navigator/index.js +94 -0
- package/src/utils/requirements-navigator/input-handler.js +217 -0
- package/src/utils/requirements-navigator/section-loader.js +188 -0
- package/src/utils/requirements-navigator/tree-builder.js +105 -0
- package/src/utils/requirements-navigator/tree-renderer.js +50 -0
- package/src/utils/requirements-navigator.js +2 -583
- package/src/utils/trui-clarifications.js +188 -0
- package/src/utils/trui-feedback.js +54 -1
- package/src/utils/trui-kiro-integration.js +398 -0
- package/src/utils/trui-main-handlers.js +194 -0
- package/src/utils/trui-main-menu.js +235 -0
- package/src/utils/trui-nav-agents.js +178 -25
- package/src/utils/trui-nav-requirements.js +203 -27
- package/src/utils/trui-nav-settings.js +114 -1
- package/src/utils/trui-nav-specifications.js +44 -3
- package/src/utils/trui-navigation-backup.js +603 -0
- package/src/utils/trui-navigation.js +70 -228
- package/src/utils/trui-provider-health.js +274 -0
- package/src/utils/trui-provider-manager.js +376 -0
- package/src/utils/trui-quick-menu.js +25 -1
- package/src/utils/trui-req-actions-backup.js +507 -0
- package/src/utils/trui-req-actions.js +148 -216
- package/src/utils/trui-req-editor.js +170 -0
- package/src/utils/trui-req-file-ops.js +278 -0
- package/src/utils/trui-req-tree-old.js +719 -0
- package/src/utils/trui-req-tree.js +348 -627
- package/src/utils/trui-specifications.js +25 -7
- package/src/utils/trui-windsurf.js +231 -10
- package/src/utils/welcome-screen-extracted.js +2 -2
- package/src/utils/welcome-screen.js +2 -2
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* OpenCode Agent Checker - Handles OpenCode CLI specific checks and configuration
|
|
10
|
+
*
|
|
11
|
+
* This module provides specialized checking for OpenCode CLI agent:
|
|
12
|
+
* 1. First attempts a simple ping test (1+1 or "ping")
|
|
13
|
+
* 2. If configuration errors occur, attempts to pre-configure with working model
|
|
14
|
+
* 3. If still stuck, attempts to automate initial configuration prompts
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
class OpenCodeChecker {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.configPath = path.join(os.homedir(), '.config', 'opencode');
|
|
20
|
+
this.globalConfigFile = path.join(this.configPath, 'opencode.json');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Check if OpenCode CLI is available in PATH
|
|
25
|
+
* @returns {boolean}
|
|
26
|
+
*/
|
|
27
|
+
isAvailable() {
|
|
28
|
+
try {
|
|
29
|
+
const { execSync } = require('child_process');
|
|
30
|
+
execSync('which opencode', { stdio: 'ignore', timeout: 5000 });
|
|
31
|
+
return true;
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Attempt a simple ping test to check if OpenCode is responsive
|
|
39
|
+
* @returns {Promise<{responsive: boolean, error?: string, needsConfig?: boolean}>}
|
|
40
|
+
*/
|
|
41
|
+
async pingTest(configOverride = null) {
|
|
42
|
+
console.log('[OPENCODE CHECK] Starting ping test...');
|
|
43
|
+
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
const env = { ...process.env };
|
|
46
|
+
if (configOverride) {
|
|
47
|
+
env.OPENCODE_CONFIG_CONTENT = configOverride;
|
|
48
|
+
console.log('[OPENCODE CHECK] Using config override:', configOverride);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const proc = spawn('opencode', ['-p', '1+1', '-q'], {
|
|
52
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
53
|
+
timeout: 30000,
|
|
54
|
+
env
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
let stdout = '';
|
|
58
|
+
let stderr = '';
|
|
59
|
+
|
|
60
|
+
proc.stdout.on('data', (data) => {
|
|
61
|
+
const chunk = data.toString();
|
|
62
|
+
stdout += chunk;
|
|
63
|
+
console.log('[OPENCODE CHECK] STDOUT:', chunk.trim());
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
proc.stderr.on('data', (data) => {
|
|
67
|
+
const chunk = data.toString();
|
|
68
|
+
stderr += chunk;
|
|
69
|
+
console.log('[OPENCODE CHECK] STDERR:', chunk.trim());
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
proc.on('close', (code) => {
|
|
73
|
+
const output = stdout + stderr;
|
|
74
|
+
console.log(`[OPENCODE CHECK] Process exited with code: ${code}`);
|
|
75
|
+
console.log(`[OPENCODE CHECK] Full output: ${output.substring(0, 500)}`);
|
|
76
|
+
|
|
77
|
+
// Check for successful response
|
|
78
|
+
if (code === 0 && (output.includes('2') || output.includes('2\n'))) {
|
|
79
|
+
console.log('[OPENCODE CHECK] ✓ Ping test successful');
|
|
80
|
+
resolve({ responsive: true });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check for configuration errors
|
|
85
|
+
const needsConfig =
|
|
86
|
+
output.includes('model') && output.includes('decommissioned') ||
|
|
87
|
+
output.includes('API key') ||
|
|
88
|
+
output.includes('configuration') ||
|
|
89
|
+
output.includes('configure') ||
|
|
90
|
+
output.includes('failed to process events') ||
|
|
91
|
+
output.includes('LSP Configuration') || // Add LSP configuration detection
|
|
92
|
+
output.includes('Unknown request URL') || // Add API endpoint detection
|
|
93
|
+
output.includes('unknown_url'); // Add API endpoint detection
|
|
94
|
+
|
|
95
|
+
if (needsConfig) {
|
|
96
|
+
console.log('[OPENCODE CHECK] ⚠ Configuration needed detected');
|
|
97
|
+
|
|
98
|
+
// Provide specific error details
|
|
99
|
+
let errorDetails = 'OpenCode needs configuration';
|
|
100
|
+
if (output.includes('Unknown request URL') || output.includes('unknown_url')) {
|
|
101
|
+
errorDetails = 'OpenCode has incorrect API endpoint configuration';
|
|
102
|
+
} else if (output.includes('max_tokens')) {
|
|
103
|
+
errorDetails = 'OpenCode has max_tokens configuration issue';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
resolve({
|
|
107
|
+
responsive: false,
|
|
108
|
+
error: errorDetails,
|
|
109
|
+
needsConfig: true,
|
|
110
|
+
details: output.substring(0, 200)
|
|
111
|
+
});
|
|
112
|
+
} else {
|
|
113
|
+
console.log('[OPENCODE CHECK] ✗ Ping test failed for unknown reason');
|
|
114
|
+
resolve({
|
|
115
|
+
responsive: false,
|
|
116
|
+
error: `OpenCode ping failed (exit code: ${code})`,
|
|
117
|
+
details: output.substring(0, 200)
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
proc.on('error', (error) => {
|
|
123
|
+
console.log(`[OPENCODE CHECK] ✗ Process error: ${error.message}`);
|
|
124
|
+
resolve({
|
|
125
|
+
responsive: false,
|
|
126
|
+
error: `Failed to run OpenCode: ${error.message}`
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Create a basic configuration with a working model
|
|
134
|
+
* @returns {Promise<{success: boolean, error?: string}>}
|
|
135
|
+
*/
|
|
136
|
+
async createBasicConfig() {
|
|
137
|
+
console.log('[OPENCODE CHECK] Attempting to create basic configuration...');
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
// Ensure config directory exists
|
|
141
|
+
fs.mkdirSync(this.configPath, { recursive: true });
|
|
142
|
+
console.log(`[OPENCODE CHECK] Config directory ensured: ${this.configPath}`);
|
|
143
|
+
|
|
144
|
+
// Try to detect available API keys from environment
|
|
145
|
+
const availableProviders = this.detectAvailableProviders();
|
|
146
|
+
console.log(`[OPENCODE CHECK] Available providers detected: ${availableProviders.join(', ')}`);
|
|
147
|
+
|
|
148
|
+
// Create config with the first available provider and a working model
|
|
149
|
+
const model = this.selectWorkingModel(availableProviders);
|
|
150
|
+
console.log(`[OPENCODE CHECK] Selected model: ${model}`);
|
|
151
|
+
|
|
152
|
+
const config = {
|
|
153
|
+
"$schema": "https://opencode.ai/config.json",
|
|
154
|
+
"model": model,
|
|
155
|
+
"autoupdate": false,
|
|
156
|
+
"server": {
|
|
157
|
+
"port": 4096
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Write the configuration
|
|
162
|
+
const configJson = JSON.stringify(config, null, 2);
|
|
163
|
+
fs.writeFileSync(this.globalConfigFile, configJson);
|
|
164
|
+
console.log(`[OPENCODE CHECK] Configuration written to: ${this.globalConfigFile}`);
|
|
165
|
+
console.log(`[OPENCODE CHECK] Config content: ${configJson}`);
|
|
166
|
+
|
|
167
|
+
return { success: true };
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.log(`[OPENCODE CHECK] ✗ Failed to create config: ${error.message}`);
|
|
170
|
+
return { success: false, error: error.message };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Detect available API providers from environment variables
|
|
176
|
+
* @returns {string[]}
|
|
177
|
+
*/
|
|
178
|
+
detectAvailableProviders() {
|
|
179
|
+
const providers = [];
|
|
180
|
+
|
|
181
|
+
if (process.env.OPENAI_API_KEY) providers.push('openai');
|
|
182
|
+
if (process.env.ANTHROPIC_API_KEY) providers.push('anthropic');
|
|
183
|
+
if (process.env.GEMINI_API_KEY) providers.push('google');
|
|
184
|
+
if (process.env.GROQ_API_KEY) providers.push('groq');
|
|
185
|
+
|
|
186
|
+
return providers;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Select a working model based on available providers
|
|
191
|
+
* @param {string[]} providers
|
|
192
|
+
* @returns {string}
|
|
193
|
+
*/
|
|
194
|
+
selectWorkingModel(providers) {
|
|
195
|
+
const modelMap = {
|
|
196
|
+
'openai': 'gpt-4o-mini',
|
|
197
|
+
'anthropic': 'claude-3-haiku-20240307',
|
|
198
|
+
'google': 'gemini-flash-1.5',
|
|
199
|
+
'groq': 'Llama3_3_70BVersatile' // Use the model shown in OpenCode's UI
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// Try providers in order of preference
|
|
203
|
+
for (const provider of ['anthropic', 'openai', 'google', 'groq']) {
|
|
204
|
+
if (providers.includes(provider)) {
|
|
205
|
+
return modelMap[provider];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Fallback to a common model (may fail but worth trying)
|
|
210
|
+
return 'Llama3_3_70BVersatile';
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Attempt to automatically configure OpenCode using keyboard shortcuts
|
|
215
|
+
* @returns {Promise<{success: boolean, message: string}>}
|
|
216
|
+
*/
|
|
217
|
+
async attemptKeyboardConfiguration() {
|
|
218
|
+
console.log('[OPENCODE CHECK] Attempting keyboard-based configuration...');
|
|
219
|
+
|
|
220
|
+
return new Promise((resolve) => {
|
|
221
|
+
// Try to use expect or node-pty to send keyboard shortcuts
|
|
222
|
+
let pty;
|
|
223
|
+
try {
|
|
224
|
+
pty = require('node-pty');
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.log('[OPENCODE CHECK] node-pty not available for keyboard automation');
|
|
227
|
+
resolve({ success: false, message: 'node-pty not available for keyboard automation' });
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const proc = pty.spawn('opencode', [], {
|
|
232
|
+
name: 'xterm-color',
|
|
233
|
+
cols: 120,
|
|
234
|
+
rows: 30,
|
|
235
|
+
cwd: process.cwd(),
|
|
236
|
+
env: process.env
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
let output = '';
|
|
240
|
+
let configured = false;
|
|
241
|
+
|
|
242
|
+
proc.on('data', (data) => {
|
|
243
|
+
output += data;
|
|
244
|
+
console.log('[OPENCODE CHECK] KEYBOARD STDOUT:', data.trim());
|
|
245
|
+
|
|
246
|
+
// Wait for OpenCode to fully load
|
|
247
|
+
if (output.includes('OpenCode') && output.includes('press enter to send')) {
|
|
248
|
+
console.log('[OPENCODE CHECK] OpenCode ready, attempting model selection...');
|
|
249
|
+
|
|
250
|
+
// Send ctrl+o to open model selection
|
|
251
|
+
proc.write('\x0f'); // ctrl+o
|
|
252
|
+
|
|
253
|
+
setTimeout(() => {
|
|
254
|
+
// Try to select a working model by typing and entering
|
|
255
|
+
proc.write('Llama3_3_70BVersatile\r');
|
|
256
|
+
|
|
257
|
+
setTimeout(() => {
|
|
258
|
+
// Try to send a test message
|
|
259
|
+
proc.write('1+1\r');
|
|
260
|
+
|
|
261
|
+
setTimeout(() => {
|
|
262
|
+
if (output.includes('2') || output.includes('2\n')) {
|
|
263
|
+
configured = true;
|
|
264
|
+
console.log('[OPENCODE CHECK] ✓ Keyboard configuration successful');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
proc.write('\x03'); // ctrl+c to quit
|
|
268
|
+
setTimeout(() => proc.destroy(), 1000);
|
|
269
|
+
}, 3000);
|
|
270
|
+
}, 2000);
|
|
271
|
+
}, 1000);
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
proc.on('exit', () => {
|
|
276
|
+
resolve({
|
|
277
|
+
success: configured,
|
|
278
|
+
message: configured ? 'Keyboard configuration successful' : 'Keyboard configuration failed - manual intervention required'
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Timeout after 15 seconds
|
|
283
|
+
setTimeout(() => {
|
|
284
|
+
if (!proc.destroyed) {
|
|
285
|
+
proc.destroy();
|
|
286
|
+
resolve({
|
|
287
|
+
success: false,
|
|
288
|
+
message: 'Keyboard configuration timed out'
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}, 15000);
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Attempt to automatically respond to LSP Configuration prompt
|
|
297
|
+
* @returns {Promise<{success: boolean, message: string}>}
|
|
298
|
+
*/
|
|
299
|
+
async attemptLSPConfiguration() {
|
|
300
|
+
console.log('[OPENCODE CHECK] Attempting to bypass LSP Configuration...');
|
|
301
|
+
|
|
302
|
+
return new Promise((resolve) => {
|
|
303
|
+
// Try to send a message to skip LSP configuration
|
|
304
|
+
const proc = spawn('opencode', ['-p', 'skip', '-q'], {
|
|
305
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
306
|
+
timeout: 15000
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
let stdout = '';
|
|
310
|
+
let stderr = '';
|
|
311
|
+
|
|
312
|
+
proc.stdout.on('data', (data) => {
|
|
313
|
+
const chunk = data.toString();
|
|
314
|
+
stdout += chunk;
|
|
315
|
+
console.log('[OPENCODE CHECK] LSP-CONFIG STDOUT:', chunk.trim());
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
proc.stderr.on('data', (data) => {
|
|
319
|
+
const chunk = data.toString();
|
|
320
|
+
stderr += chunk;
|
|
321
|
+
console.log('[OPENCODE CHECK] LSP-CONFIG STDERR:', chunk.trim());
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
proc.on('close', (code) => {
|
|
325
|
+
const output = stdout + stderr;
|
|
326
|
+
console.log(`[OPENCODE CHECK] LSP config process exited with code: ${code}`);
|
|
327
|
+
|
|
328
|
+
// If we get a successful response or it moves past the LSP config
|
|
329
|
+
if (code === 0 && !output.includes('LSP Configuration')) {
|
|
330
|
+
console.log('[OPENCODE CHECK] ✓ LSP Configuration bypassed');
|
|
331
|
+
resolve({ success: true, message: 'LSP Configuration bypassed successfully' });
|
|
332
|
+
} else {
|
|
333
|
+
console.log('[OPENCODE CHECK] ✗ LSP Configuration still blocking');
|
|
334
|
+
resolve({ success: false, message: 'LSP Configuration still requires manual intervention' });
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
proc.on('error', (error) => {
|
|
339
|
+
console.log(`[OPENCODE CHECK] LSP config error: ${error.message}`);
|
|
340
|
+
resolve({ success: false, message: `LSP config error: ${error.message}` });
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Test if OpenCode interactive mode works (to detect configuration mismatch)
|
|
347
|
+
* @returns {Promise<{interactiveWorks: boolean, error?: string}>}
|
|
348
|
+
*/
|
|
349
|
+
async testInteractiveMode() {
|
|
350
|
+
console.log('[OPENCODE CHECK] Testing interactive mode vs non-interactive mode...');
|
|
351
|
+
|
|
352
|
+
return new Promise((resolve) => {
|
|
353
|
+
const proc = spawn('opencode', ['-d'], {
|
|
354
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
355
|
+
timeout: 5000
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
let stdout = '';
|
|
359
|
+
let stderr = '';
|
|
360
|
+
let interactiveReady = false;
|
|
361
|
+
|
|
362
|
+
proc.stdout.on('data', (data) => {
|
|
363
|
+
const chunk = data.toString();
|
|
364
|
+
stdout += chunk;
|
|
365
|
+
console.log('[OPENCODE CHECK] INTERACTIVE STDOUT:', chunk.trim());
|
|
366
|
+
|
|
367
|
+
// Check if interactive mode loads successfully and has the correct model
|
|
368
|
+
if (stdout.includes('OpenCode') && (stdout.includes('press enter to send') || stdout.includes('ctrl+? help'))) {
|
|
369
|
+
interactiveReady = true;
|
|
370
|
+
console.log('[OPENCODE CHECK] ✓ Interactive mode ready');
|
|
371
|
+
if (stdout.includes('Llama3_3_70BVersatile')) {
|
|
372
|
+
console.log('[OPENCODE CHECK] ✓ Correct model detected in interactive mode');
|
|
373
|
+
}
|
|
374
|
+
proc.kill('SIGTERM');
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
proc.stderr.on('data', (data) => {
|
|
379
|
+
const chunk = data.toString();
|
|
380
|
+
stderr += chunk;
|
|
381
|
+
console.log('[OPENCODE CHECK] INTERACTIVE STDERR:', chunk.trim());
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
const timeout = setTimeout(() => {
|
|
385
|
+
proc.kill('SIGTERM');
|
|
386
|
+
}, 4000);
|
|
387
|
+
|
|
388
|
+
proc.on('close', (code) => {
|
|
389
|
+
clearTimeout(timeout);
|
|
390
|
+
|
|
391
|
+
if (interactiveReady) {
|
|
392
|
+
resolve({ interactiveWorks: true });
|
|
393
|
+
} else {
|
|
394
|
+
resolve({
|
|
395
|
+
interactiveWorks: false,
|
|
396
|
+
error: 'Interactive mode failed to load properly'
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
proc.on('error', (error) => {
|
|
402
|
+
clearTimeout(timeout);
|
|
403
|
+
resolve({
|
|
404
|
+
interactiveWorks: false,
|
|
405
|
+
error: `Failed to start interactive mode: ${error.message}`
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Attempt to run OpenCode in interactive mode and detect configuration prompts
|
|
413
|
+
* @returns {Promise<{success: boolean, configured: boolean, error?: string}>}
|
|
414
|
+
*/
|
|
415
|
+
async attemptAutoConfiguration() {
|
|
416
|
+
console.log('[OPENCODE CHECK] Attempting to detect auto-configuration prompts...');
|
|
417
|
+
|
|
418
|
+
return new Promise((resolve) => {
|
|
419
|
+
const proc = spawn('opencode', ['-d'], {
|
|
420
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
421
|
+
timeout: 10000
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
let stdout = '';
|
|
425
|
+
let stderr = '';
|
|
426
|
+
let detectedPrompt = false;
|
|
427
|
+
|
|
428
|
+
proc.stdout.on('data', (data) => {
|
|
429
|
+
const chunk = data.toString();
|
|
430
|
+
stdout += chunk;
|
|
431
|
+
console.log('[OPENCODE CHECK] AUTO-CONFIG STDOUT:', chunk.trim());
|
|
432
|
+
|
|
433
|
+
// Detect common configuration prompts
|
|
434
|
+
if (stdout.includes('Select a model') ||
|
|
435
|
+
stdout.includes('Choose a provider') ||
|
|
436
|
+
stdout.includes('API key') ||
|
|
437
|
+
stdout.includes('Configure') ||
|
|
438
|
+
stdout.includes('LSP Configuration')) {
|
|
439
|
+
detectedPrompt = true;
|
|
440
|
+
console.log('[OPENCODE CHECK] ⚠ Configuration prompt detected');
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
proc.stderr.on('data', (data) => {
|
|
445
|
+
const chunk = data.toString();
|
|
446
|
+
stderr += chunk;
|
|
447
|
+
console.log('[OPENCODE CHECK] AUTO-CONFIG STDERR:', chunk.trim());
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Kill the process after timeout or when prompt is detected
|
|
451
|
+
const timeout = setTimeout(() => {
|
|
452
|
+
console.log('[OPENCODE CHECK] Timeout reached, killing process...');
|
|
453
|
+
proc.kill('SIGTERM');
|
|
454
|
+
}, 8000);
|
|
455
|
+
|
|
456
|
+
proc.on('close', (code) => {
|
|
457
|
+
clearTimeout(timeout);
|
|
458
|
+
|
|
459
|
+
if (detectedPrompt) {
|
|
460
|
+
resolve({
|
|
461
|
+
success: false,
|
|
462
|
+
configured: false,
|
|
463
|
+
error: 'OpenCode is waiting for initial configuration. Please run `opencode` interactively to complete setup.',
|
|
464
|
+
needsManualConfig: true
|
|
465
|
+
});
|
|
466
|
+
} else if (stdout.includes('Error') || stderr.includes('Error')) {
|
|
467
|
+
resolve({
|
|
468
|
+
success: false,
|
|
469
|
+
configured: false,
|
|
470
|
+
error: 'OpenCode configuration error detected'
|
|
471
|
+
});
|
|
472
|
+
} else {
|
|
473
|
+
resolve({
|
|
474
|
+
success: true,
|
|
475
|
+
configured: true
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
proc.on('error', (error) => {
|
|
481
|
+
clearTimeout(timeout);
|
|
482
|
+
resolve({
|
|
483
|
+
success: false,
|
|
484
|
+
configured: false,
|
|
485
|
+
error: `Failed to start OpenCode: ${error.message}`
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Run the complete OpenCode check flow
|
|
493
|
+
* @returns {Promise<{status: string, message: string, checkedAt: string}>}
|
|
494
|
+
*/
|
|
495
|
+
async check() {
|
|
496
|
+
const checkedAt = new Date().toISOString();
|
|
497
|
+
|
|
498
|
+
// 1. Check if OpenCode is available
|
|
499
|
+
if (!this.isAvailable()) {
|
|
500
|
+
return {
|
|
501
|
+
status: 'error',
|
|
502
|
+
message: 'OpenCode CLI not installed. Install from https://opencode.ai',
|
|
503
|
+
checkedAt
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// 2. Try ping test first
|
|
508
|
+
const pingResult = await this.pingTest();
|
|
509
|
+
if (pingResult.responsive) {
|
|
510
|
+
return {
|
|
511
|
+
status: 'success',
|
|
512
|
+
message: 'OpenCode is responsive and configured',
|
|
513
|
+
checkedAt
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// 3. If ping failed due to configuration, try to create basic config
|
|
518
|
+
if (pingResult.needsConfig) {
|
|
519
|
+
console.log('🔧 OpenCode needs configuration, attempting to setup...');
|
|
520
|
+
|
|
521
|
+
const configResult = await this.createBasicConfig();
|
|
522
|
+
if (configResult.success) {
|
|
523
|
+
// Create config override string
|
|
524
|
+
const availableProviders = this.detectAvailableProviders();
|
|
525
|
+
const model = this.selectWorkingModel(availableProviders);
|
|
526
|
+
const configOverride = JSON.stringify({
|
|
527
|
+
"$schema": "https://opencode.ai/config.json",
|
|
528
|
+
"model": model,
|
|
529
|
+
"autoupdate": false,
|
|
530
|
+
"server": { "port": 4096 }
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
console.log('[OPENCODE CHECK] Trying with config override...');
|
|
534
|
+
const retryPing = await this.pingTest(configOverride);
|
|
535
|
+
if (retryPing.responsive) {
|
|
536
|
+
return {
|
|
537
|
+
status: 'success',
|
|
538
|
+
message: 'OpenCode configured and responsive (with config override)',
|
|
539
|
+
checkedAt
|
|
540
|
+
};
|
|
541
|
+
} else if (retryPing.needsConfig) {
|
|
542
|
+
// Still needs config - provide specific guidance based on what we know
|
|
543
|
+
console.log('🔧 Non-interactive mode has max_tokens issues, but interactive mode may work...');
|
|
544
|
+
|
|
545
|
+
return {
|
|
546
|
+
status: 'error',
|
|
547
|
+
message: 'OpenCode has max_tokens configuration issues with the selected Groq model. Both interactive and non-interactive modes are affected. Please try: 1) Select a different model with Ctrl+o, 2) Check your GROQ_API_KEY quota, 3) Update OpenCode, or 4) Use a different provider.',
|
|
548
|
+
checkedAt
|
|
549
|
+
};
|
|
550
|
+
} else {
|
|
551
|
+
// Configuration created but still not working
|
|
552
|
+
return {
|
|
553
|
+
status: 'error',
|
|
554
|
+
message: `OpenCode configured but still not responding: ${retryPing.error}`,
|
|
555
|
+
checkedAt
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
} else {
|
|
559
|
+
return {
|
|
560
|
+
status: 'error',
|
|
561
|
+
message: `Failed to configure OpenCode: ${configResult.error}`,
|
|
562
|
+
checkedAt
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// 4. If we get here, try to detect if it's stuck in initial setup
|
|
568
|
+
const autoConfigResult = await this.attemptAutoConfiguration();
|
|
569
|
+
if (autoConfigResult.needsManualConfig) {
|
|
570
|
+
return {
|
|
571
|
+
status: 'error',
|
|
572
|
+
message: 'OpenCode requires manual configuration. Interactive mode works but non-interactive mode has max_tokens issues. Please run "opencode" interactively to complete setup, then try the agent check again.',
|
|
573
|
+
checkedAt
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// 5. Generic failure
|
|
578
|
+
return {
|
|
579
|
+
status: 'error',
|
|
580
|
+
message: pingResult.error || 'OpenCode check failed',
|
|
581
|
+
checkedAt
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
module.exports = {
|
|
587
|
+
OpenCodeChecker
|
|
588
|
+
};
|
|
@@ -77,9 +77,67 @@ async function checkProvider(providerId, config = {}, repoPath, onProgress = nul
|
|
|
77
77
|
|
|
78
78
|
const resultFile = getResultFilePath(repoPath);
|
|
79
79
|
|
|
80
|
+
// Special handling: VS Code Copilot CLI is often installed but not authenticated.
|
|
81
|
+
// Detect that case explicitly so the UI can prompt for configuration instead of showing rate limit.
|
|
82
|
+
if (providerId === 'vscode-copilot-cli') {
|
|
83
|
+
try {
|
|
84
|
+
if (onProgress) onProgress(providerId, 'configuring', null, 'Checking configuration...');
|
|
85
|
+
const DirectLLMManager = require('vibecodingmachine-core/src/llm/direct-llm-manager.cjs');
|
|
86
|
+
const mgr = new DirectLLMManager();
|
|
87
|
+
const availability = await mgr.isVSCodeCopilotCLIAvailable();
|
|
88
|
+
|
|
89
|
+
if (!availability || !availability.available) {
|
|
90
|
+
return {
|
|
91
|
+
status: 'error',
|
|
92
|
+
message: `${def.name} is not installed or not in PATH. Install GitHub Copilot CLI and try again.`,
|
|
93
|
+
checkedAt: new Date().toISOString(),
|
|
94
|
+
requirementLeftPending: false,
|
|
95
|
+
needsConfiguration: true
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (availability.needsAuth) {
|
|
100
|
+
return {
|
|
101
|
+
status: 'error',
|
|
102
|
+
message: `${def.name} is installed but not authenticated. Run 'copilot login' (or authenticate GitHub CLI and restart) then try again.`,
|
|
103
|
+
checkedAt: new Date().toISOString(),
|
|
104
|
+
requirementLeftPending: false,
|
|
105
|
+
needsConfiguration: true
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Check if rate limited
|
|
110
|
+
if (availability.rateLimited) {
|
|
111
|
+
return {
|
|
112
|
+
status: 'error',
|
|
113
|
+
message: `${def.name} is rate limited. Quota reset should happen soon.`,
|
|
114
|
+
checkedAt: new Date().toISOString(),
|
|
115
|
+
requirementLeftPending: false,
|
|
116
|
+
rateLimited: true
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Copilot CLI is available and authenticated - return success immediately
|
|
121
|
+
return {
|
|
122
|
+
status: 'success',
|
|
123
|
+
message: `${def.name} is working correctly (tested with copilot -p "Reply with OK" -s --no-ask-user)`,
|
|
124
|
+
checkedAt: new Date().toISOString(),
|
|
125
|
+
requirementLeftPending: false
|
|
126
|
+
};
|
|
127
|
+
} catch (err) {
|
|
128
|
+
return {
|
|
129
|
+
status: 'error',
|
|
130
|
+
message: `${def.name} configuration check failed: ${err.message}`,
|
|
131
|
+
checkedAt: new Date().toISOString(),
|
|
132
|
+
requirementLeftPending: false,
|
|
133
|
+
needsConfiguration: true
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
80
138
|
// Ensure the config has repoPath set for the child process
|
|
81
139
|
try {
|
|
82
|
-
const { getRepoPath, setRepoPath } = require('
|
|
140
|
+
const { getRepoPath, setRepoPath } = require('../config');
|
|
83
141
|
const currentRepoPath = await getRepoPath();
|
|
84
142
|
if (currentRepoPath !== repoPath) {
|
|
85
143
|
console.log(`[AGENT CHECK] Setting main config repoPath to: ${repoPath}`);
|
|
@@ -96,11 +154,38 @@ async function checkProvider(providerId, config = {}, repoPath, onProgress = nul
|
|
|
96
154
|
// For direct CLI providers: auto-install if missing
|
|
97
155
|
const cliInfo = CLI_AUTO_INSTALL[providerId];
|
|
98
156
|
if (cliInfo && !isCLIAvailable(cliInfo.cmd, providerId)) {
|
|
99
|
-
if (onProgress)
|
|
157
|
+
if (onProgress) {
|
|
158
|
+
onProgress(providerId, 'installing');
|
|
159
|
+
}
|
|
160
|
+
|
|
100
161
|
const { installed, note } = await installCLI(providerId);
|
|
101
162
|
if (!installed) {
|
|
102
163
|
return { status: 'error', message: `${def.name} not installed and auto-install failed: ${note}`, checkedAt: new Date().toISOString(), requirementLeftPending: false };
|
|
103
164
|
}
|
|
165
|
+
|
|
166
|
+
if (onProgress) {
|
|
167
|
+
onProgress(providerId, 'installing', null, 'Installation complete, configuring...');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Special handling for OpenCode - use specialized checker
|
|
172
|
+
if (providerId === 'opencode') {
|
|
173
|
+
const { OpenCodeChecker } = require('./opencode-checker');
|
|
174
|
+
const checker = new OpenCodeChecker();
|
|
175
|
+
const result = await checker.check();
|
|
176
|
+
|
|
177
|
+
if (result.status === 'success') {
|
|
178
|
+
// OpenCode is responsive, create the result file to indicate success
|
|
179
|
+
try {
|
|
180
|
+
fs.writeFileSync(resultFile, 'VCM_CHECK_OK', 'utf8');
|
|
181
|
+
} catch (err) {
|
|
182
|
+
console.warn(`[AGENT CHECK] Could not write result file: ${err.message}`);
|
|
183
|
+
}
|
|
184
|
+
return { ...result, requirementLeftPending: false };
|
|
185
|
+
} else {
|
|
186
|
+
// OpenCode failed - leave requirement pending for other agents
|
|
187
|
+
return { ...result, requirementLeftPending: true };
|
|
188
|
+
}
|
|
104
189
|
}
|
|
105
190
|
|
|
106
191
|
// Add test requirement (idempotent — safe to call even if already present from a prior failed agent)
|
|
@@ -137,7 +222,7 @@ async function checkAllProviders(providerIds, config = {}, repoPath, onProgress
|
|
|
137
222
|
if (signal && signal.cancelled) break;
|
|
138
223
|
if (onProgress) onProgress(id, 'checking');
|
|
139
224
|
results[id] = await checkProvider(id, config, repoPath, onProgress, signal);
|
|
140
|
-
if (onProgress) onProgress(id, 'done', results[id]);
|
|
225
|
+
if (onProgress) onProgress(id, 'done', results[id].status === 'error' ? results[id].message : null);
|
|
141
226
|
}
|
|
142
227
|
|
|
143
228
|
// Final cleanup — remove requirement if still pending (all agents failed or skipped)
|