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,266 @@
1
+ /**
2
+ * File scanning functions for auto-direct command
3
+ * Extracted from auto-direct.js to reduce file size
4
+ */
5
+
6
+ const fs = require('fs-extra');
7
+ const path = require('path');
8
+ const chalk = require('chalk');
9
+
10
+ /**
11
+ * Find relevant files based on requirement
12
+ */
13
+ async function findRelevantFiles(requirement, repoPath) {
14
+ const relevantFiles = [];
15
+
16
+ try {
17
+ // Common file patterns to search for
18
+ const patterns = [
19
+ '**/*.js',
20
+ '**/*.jsx',
21
+ '**/*.ts',
22
+ '**/*.tsx',
23
+ '**/*.json',
24
+ '**/*.md',
25
+ '**/*.yml',
26
+ '**/*.yaml'
27
+ ];
28
+
29
+ // Keywords to look for in requirement
30
+ const keywords = extractKeywords(requirement);
31
+
32
+ // Search for files containing keywords
33
+ for (const pattern of patterns) {
34
+ const files = await glob(pattern, { cwd: repoPath, ignore: ['**/node_modules/**', '**/.git/**'] });
35
+
36
+ for (const file of files) {
37
+ const fullPath = path.join(repoPath, file);
38
+
39
+ // Skip if not a file
40
+ if (!await fs.pathExists(fullPath) || !await fs.stat(fullPath).then(stat => stat.isFile())) {
41
+ continue;
42
+ }
43
+
44
+ // Check if file contains relevant keywords
45
+ const isRelevant = await isFileRelevant(fullPath, keywords);
46
+ if (isRelevant) {
47
+ relevantFiles.push(file);
48
+ }
49
+ }
50
+ }
51
+
52
+ // Remove duplicates and sort by relevance
53
+ const uniqueFiles = [...new Set(relevantFiles)];
54
+ return uniqueFiles.slice(0, 20); // Limit to top 20 files
55
+
56
+ } catch (error) {
57
+ console.error('Error finding relevant files:', error.message);
58
+ return [];
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Read file snippets to give LLM context
64
+ */
65
+ async function readFileSnippets(files, repoPath, requirement) {
66
+ const snippets = [];
67
+
68
+ for (const file of files) {
69
+ try {
70
+ const fullPath = path.join(repoPath, file);
71
+
72
+ if (!await fs.pathExists(fullPath)) {
73
+ continue;
74
+ }
75
+
76
+ const content = await fs.readFile(fullPath, 'utf8');
77
+ const lines = content.split('\n');
78
+
79
+ // For large files, find relevant sections
80
+ let startLine = 0;
81
+ let endLine = lines.length;
82
+
83
+ if (lines.length > 100) {
84
+ // Try to find relevant sections based on requirement keywords
85
+ const keywords = extractKeywords(requirement);
86
+ const relevantSections = findRelevantSections(lines, keywords);
87
+
88
+ if (relevantSections.length > 0) {
89
+ // Use the most relevant section
90
+ const section = relevantSections[0];
91
+ startLine = Math.max(0, section.start - 10); // Add context
92
+ endLine = Math.min(lines.length, section.end + 10);
93
+ } else {
94
+ // Fall back to beginning of file
95
+ endLine = 50;
96
+ }
97
+ }
98
+
99
+ const snippet = lines.slice(startLine, endLine).join('\n');
100
+
101
+ snippets.push({
102
+ file,
103
+ content: snippet,
104
+ startLine: startLine + 1,
105
+ endLine: endLine,
106
+ totalLines: lines.length
107
+ });
108
+
109
+ } catch (error) {
110
+ console.warn(`Warning: Could not read file ${file}:`, error.message);
111
+ }
112
+ }
113
+
114
+ return snippets;
115
+ }
116
+
117
+ /**
118
+ * Extract keywords from requirement text
119
+ */
120
+ function extractKeywords(requirement) {
121
+ // Simple keyword extraction - look for technical terms
122
+ const technicalTerms = [
123
+ 'function', 'class', 'component', 'module', 'package', 'file',
124
+ 'test', 'spec', 'config', 'build', 'deploy', 'api', 'endpoint',
125
+ 'database', 'model', 'view', 'controller', 'service', 'utility',
126
+ 'helper', 'constant', 'variable', 'method', 'property', 'interface'
127
+ ];
128
+
129
+ const words = requirement.toLowerCase().split(/\s+/);
130
+ const keywords = new Set();
131
+
132
+ // Add technical terms found in requirement
133
+ words.forEach(word => {
134
+ if (technicalTerms.includes(word) || word.length > 5) {
135
+ keywords.add(word);
136
+ }
137
+ });
138
+
139
+ // Look for file extensions and patterns
140
+ const filePatterns = requirement.match(/\b\w+\.(js|jsx|ts|tsx|json|md|yml|yaml)\b/g);
141
+ if (filePatterns) {
142
+ filePatterns.forEach(pattern => keywords.add(pattern));
143
+ }
144
+
145
+ // Look for specific file names in quotes
146
+ const quotedFiles = requirement.match(/['"`]([^'"`]+\.(js|jsx|ts|tsx|json|md|yml|yaml))['"`]/g);
147
+ if (quotedFiles) {
148
+ quotedFiles.forEach(file => keywords.add(file.replace(/['"`]/g, '')));
149
+ }
150
+
151
+ return Array.from(keywords);
152
+ }
153
+
154
+ /**
155
+ * Check if file contains relevant keywords
156
+ */
157
+ async function isFileRelevant(filePath, keywords) {
158
+ try {
159
+ const content = await fs.readFile(filePath, 'utf8');
160
+ const lowerContent = content.toLowerCase();
161
+
162
+ // Check if any keywords are present in the file
163
+ return keywords.some(keyword =>
164
+ lowerContent.includes(keyword.toLowerCase())
165
+ );
166
+ } catch (error) {
167
+ return false;
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Find relevant sections in file based on keywords
173
+ */
174
+ function findRelevantSections(lines, keywords) {
175
+ const sections = [];
176
+
177
+ keywords.forEach(keyword => {
178
+ const lowerKeyword = keyword.toLowerCase();
179
+
180
+ lines.forEach((line, index) => {
181
+ if (line.toLowerCase().includes(lowerKeyword)) {
182
+ // Found a keyword, create a section around it
183
+ const start = Math.max(0, index - 5);
184
+ const end = Math.min(lines.length, index + 15);
185
+
186
+ sections.push({
187
+ keyword,
188
+ start,
189
+ end,
190
+ line: index + 1,
191
+ content: line.trim()
192
+ });
193
+ }
194
+ });
195
+ });
196
+
197
+ // Sort by keyword relevance (prefer function/class definitions)
198
+ sections.sort((a, b) => {
199
+ const aScore = calculateRelevanceScore(a.content);
200
+ const bScore = calculateRelevanceScore(b.content);
201
+ return bScore - aScore;
202
+ });
203
+
204
+ return sections;
205
+ }
206
+
207
+ /**
208
+ * Calculate relevance score for a line
209
+ */
210
+ function calculateRelevanceScore(line) {
211
+ let score = 0;
212
+
213
+ // Higher score for function/class definitions
214
+ if (line.includes('function ') || line.includes('class ') || line.includes('const ')) {
215
+ score += 10;
216
+ }
217
+
218
+ // Higher score for exports
219
+ if (line.includes('module.exports') || line.includes('export ')) {
220
+ score += 8;
221
+ }
222
+
223
+ // Higher score for imports/requires
224
+ if (line.includes('require(') || line.includes('import ')) {
225
+ score += 5;
226
+ }
227
+
228
+ // Higher score for comments
229
+ if (line.includes('//') || line.includes('*') || line.includes('/*')) {
230
+ score += 3;
231
+ }
232
+
233
+ return score;
234
+ }
235
+
236
+ /**
237
+ * Simple glob implementation (or you can use the 'glob' package)
238
+ */
239
+ async function glob(pattern, options = {}) {
240
+ const { cwd = process.cwd(), ignore = [] } = options;
241
+ const { promisify } = require('util');
242
+ const { exec } = require('child_process');
243
+
244
+ try {
245
+ const { stdout } = await promisify(exec)(`find . -name "${pattern.replace('**/', '*')}"`, { cwd });
246
+ let files = stdout.trim().split('\n').filter(file => file);
247
+
248
+ // Apply ignore patterns
249
+ ignore.forEach(ignorePattern => {
250
+ files = files.filter(file => !file.includes(ignorePattern.replace('**/', '')));
251
+ });
252
+
253
+ return files;
254
+ } catch (error) {
255
+ return [];
256
+ }
257
+ }
258
+
259
+ module.exports = {
260
+ findRelevantFiles,
261
+ readFileSnippets,
262
+ extractKeywords,
263
+ isFileRelevant,
264
+ findRelevantSections,
265
+ calculateRelevanceScore
266
+ };
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Provider configuration functions for auto-direct command
3
+ * Extracted from auto-direct.js to reduce file size
4
+ */
5
+
6
+ const { getAutoConfig } = require('../../utils/config');
7
+ const { getProviderPreferences, getProviderDefinition, saveProviderPreferences } = require('../../utils/provider-registry');
8
+ const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
9
+ const { checkAntigravityRateLimit, handleAntigravityRateLimit } = require('../../utils/antigravity-js-handler');
10
+ const { checkKiroRateLimit, handleKiroRateLimit } = require('../../utils/kiro-js-handler');
11
+ const { checkClineRateLimit, handleClineRateLimit } = require('../../utils/cline-js-handler');
12
+
13
+ // CRITICAL: Shared ProviderManager instance to track rate limits across all function calls
14
+ const sharedProviderManager = new ProviderManager();
15
+
16
+ /**
17
+ * Get all available provider configurations
18
+ */
19
+ async function getAllAvailableProviders() {
20
+ const config = await getAutoConfig();
21
+ const prefs = await getProviderPreferences();
22
+
23
+ const providers = [];
24
+ const skipped = [];
25
+
26
+ // Check each provider in config
27
+ for (const [providerName, providerConfig] of Object.entries(config.providers || {})) {
28
+ // Skip if provider is disabled in preferences
29
+ if (prefs.disabled?.includes(providerName)) {
30
+ skipped.push(providerName);
31
+ continue;
32
+ }
33
+
34
+ // Check if provider is properly configured
35
+ const definition = getProviderDefinition(providerName);
36
+ if (!definition) {
37
+ skipped.push(providerName);
38
+ continue;
39
+ }
40
+
41
+ // Check required credentials
42
+ let hasCredentials = true;
43
+ if (definition.credentials) {
44
+ for (const cred of definition.credentials) {
45
+ if (!providerConfig[cred]) {
46
+ hasCredentials = false;
47
+ break;
48
+ }
49
+ }
50
+ }
51
+
52
+ if (hasCredentials) {
53
+ providers.push({
54
+ name: providerName,
55
+ displayName: definition.displayName || providerName,
56
+ config: providerConfig,
57
+ definition
58
+ });
59
+ } else {
60
+ skipped.push(providerName);
61
+ }
62
+ }
63
+
64
+ return { providers, skipped };
65
+ }
66
+
67
+ /**
68
+ * Get provider configuration with automatic failover
69
+ */
70
+ async function getProviderConfig(excludeProvider = null) {
71
+ const config = await getAutoConfig();
72
+ const providerManager = sharedProviderManager; // Use shared instance to persist rate limit state
73
+ const { providers, skipped } = await getAllAvailableProviders();
74
+
75
+ // Filter out excluded provider
76
+ const availableProviders = providers.filter(p => p.name !== excludeProvider);
77
+
78
+ if (availableProviders.length === 0) {
79
+ throw new Error('No providers available. Check your configuration.');
80
+ }
81
+
82
+ // Sort providers by reliability (health score)
83
+ const sortedProviders = availableProviders.sort((a, b) => {
84
+ const aHealth = providerManager.getHealthScore(a.name);
85
+ const bHealth = providerManager.getHealthScore(b.name);
86
+ return bHealth - aHealth; // Higher health score first
87
+ });
88
+
89
+ // Try providers in order of reliability
90
+ for (const provider of sortedProviders) {
91
+ // Check rate limits for specific providers
92
+ if (provider.name === 'antigravity') {
93
+ const isRateLimited = checkAntigravityRateLimit();
94
+ if (isRateLimited) {
95
+ console.warn(`⚠️ ${provider.displayName} is rate limited, trying next provider`);
96
+ handleAntigravityRateLimit();
97
+ continue;
98
+ }
99
+ } else if (provider.name === 'kiro') {
100
+ const isRateLimited = checkKiroRateLimit();
101
+ if (isRateLimited) {
102
+ console.warn(`⚠️ ${provider.displayName} is rate limited, trying next provider`);
103
+ handleKiroRateLimit();
104
+ continue;
105
+ }
106
+ } else if (provider.name === 'cline') {
107
+ const isRateLimited = checkClineRateLimit();
108
+ if (isRateLimited) {
109
+ console.warn(`⚠️ ${provider.displayName} is rate limited, trying next provider`);
110
+ handleClineRateLimit();
111
+ continue;
112
+ }
113
+ }
114
+
115
+ return {
116
+ provider: provider.name,
117
+ displayName: provider.displayName,
118
+ config: provider.config,
119
+ definition: provider.definition
120
+ };
121
+ }
122
+
123
+ throw new Error('All providers are rate limited or unavailable');
124
+ }
125
+
126
+ /**
127
+ * Acquire provider configuration with enhanced failover and model selection
128
+ */
129
+ async function acquireProviderConfig(excludeProvider = null, excludeModel = null, forcedProvider = null) {
130
+ // If a specific provider is forced, bypass normal selection
131
+ if (forcedProvider) {
132
+ const { providers } = await getAllAvailableProviders();
133
+ const provider = providers.find(p => p.name === forcedProvider);
134
+
135
+ if (!provider) {
136
+ throw new Error(`Forced provider "${forcedProvider}" not available`);
137
+ }
138
+
139
+ return {
140
+ provider: provider.name,
141
+ displayName: provider.displayName,
142
+ config: provider.config,
143
+ definition: provider.definition,
144
+ model: provider.config.model || provider.definition.defaultModel
145
+ };
146
+ }
147
+
148
+ const config = await getProviderConfig(excludeProvider);
149
+
150
+ // Model selection logic
151
+ let selectedModel = config.config.model;
152
+
153
+ // Exclude specific model if requested
154
+ if (excludeModel && selectedModel === excludeModel) {
155
+ // Try alternative models
156
+ const alternativeModels = config.definition.models || [];
157
+ const availableModel = alternativeModels.find(m => m !== excludeModel && m !== selectedModel);
158
+
159
+ if (availableModel) {
160
+ selectedModel = availableModel;
161
+ } else {
162
+ // If no alternative model, try next provider
163
+ return acquireProviderConfig(config.provider, excludeModel, null);
164
+ }
165
+ }
166
+
167
+ return {
168
+ ...config,
169
+ model: selectedModel
170
+ };
171
+ }
172
+
173
+ module.exports = {
174
+ getAllAvailableProviders,
175
+ getProviderConfig,
176
+ acquireProviderConfig,
177
+ sharedProviderManager
178
+ };
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Provider management functions for auto-direct command
3
+ * Extracted from auto-direct.js to reduce file size
4
+ */
5
+
6
+ const { getAutoConfig } = require('../../utils/config');
7
+ const { getProviderPreferences, getProviderDefinition, saveProviderPreferences } = require('../../utils/provider-registry');
8
+ const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
9
+ const { checkAntigravityRateLimit, handleAntigravityRateLimit } = require('../../utils/antigravity-js-handler');
10
+ const { checkKiroRateLimit, handleKiroRateLimit } = require('../../utils/kiro-js-handler');
11
+ const { checkClineRateLimit, handleClineRateLimit } = require('../../utils/cline-js-handler');
12
+ const { DirectLLMManager } = require('vibecodingmachine-core');
13
+
14
+ // CRITICAL: Shared ProviderManager instance to track rate limits across all function calls
15
+ const sharedProviderManager = new ProviderManager();
16
+
17
+ /**
18
+ * Get all available provider configurations
19
+ */
20
+ async function getAllAvailableProviders() {
21
+ const config = await getAutoConfig();
22
+ const prefs = await getProviderPreferences();
23
+
24
+ const providers = [];
25
+ const skipped = [];
26
+
27
+ // Check each configured provider
28
+ for (const [providerName, providerConfig] of Object.entries(config.providers || {})) {
29
+ if (!providerConfig.enabled) {
30
+ skipped.push(`${providerName} (disabled)`);
31
+ continue;
32
+ }
33
+
34
+ // Check rate limits for specific providers
35
+ let isRateLimited = false;
36
+ let rateLimitReason = '';
37
+
38
+ if (providerName === 'antigravity') {
39
+ const rateLimitCheck = await checkAntigravityRateLimit();
40
+ if (rateLimitCheck.isLimited) {
41
+ isRateLimited = true;
42
+ rateLimitReason = rateLimitCheck.reason;
43
+ }
44
+ } else if (providerName === 'kiro') {
45
+ const rateLimitCheck = await checkKiroRateLimit();
46
+ if (rateLimitCheck.isLimited) {
47
+ isRateLimited = true;
48
+ rateLimitReason = rateLimitCheck.reason;
49
+ }
50
+ } else if (providerName === 'cline') {
51
+ const rateLimitCheck = await checkClineRateLimit();
52
+ if (rateLimitCheck.isLimited) {
53
+ isRateLimited = true;
54
+ rateLimitReason = rateLimitCheck.reason;
55
+ }
56
+ }
57
+
58
+ if (isRateLimited) {
59
+ skipped.push(`${providerName} (${rateLimitReason})`);
60
+ continue;
61
+ }
62
+
63
+ // Get provider definition for display name
64
+ const providerDef = getProviderDefinition(providerName);
65
+ const displayName = providerDef?.displayName || providerName;
66
+
67
+ providers.push({
68
+ name: providerName,
69
+ displayName,
70
+ config: providerConfig,
71
+ models: providerConfig.models || []
72
+ });
73
+ }
74
+
75
+ return { providers, skipped };
76
+ }
77
+
78
+ /**
79
+ * Get provider configuration with automatic failover
80
+ */
81
+ async function getProviderConfig(excludeProvider = null) {
82
+ const config = await getAutoConfig();
83
+ const providerManager = sharedProviderManager; // Use shared instance to persist rate limit state
84
+ const { providers, skipped } = await getAllAvailableProviders();
85
+
86
+ // Filter out excluded provider
87
+ const availableProviders = providers.filter(p => p.name !== excludeProvider);
88
+
89
+ if (availableProviders.length === 0) {
90
+ return {
91
+ success: false,
92
+ error: `No available providers. Skipped: ${skipped.join(', ')}`
93
+ };
94
+ }
95
+
96
+ // Try providers in order
97
+ for (const provider of availableProviders) {
98
+ try {
99
+ // Check if provider is rate limited through ProviderManager
100
+ const rateLimitStatus = providerManager.getRateLimitStatus(provider.name);
101
+ if (rateLimitStatus.isLimited) {
102
+ continue;
103
+ }
104
+
105
+ // Select first available model
106
+ const availableModels = provider.models.filter(m => m.enabled);
107
+ if (availableModels.length === 0) {
108
+ continue;
109
+ }
110
+
111
+ const selectedModel = availableModels[0];
112
+
113
+ return {
114
+ success: true,
115
+ provider: provider.name,
116
+ ide: provider.name,
117
+ displayName: provider.displayName,
118
+ model: selectedModel.name,
119
+ config: {
120
+ ...provider.config,
121
+ model: selectedModel
122
+ }
123
+ };
124
+ } catch (error) {
125
+ console.warn(`Failed to configure provider ${provider.name}:`, error.message);
126
+ continue;
127
+ }
128
+ }
129
+
130
+ return {
131
+ success: false,
132
+ error: `All providers failed. Skipped: ${skipped.join(', ')}`
133
+ };
134
+ }
135
+
136
+ /**
137
+ * Acquire provider configuration with enhanced error handling
138
+ */
139
+ async function acquireProviderConfig(excludeProvider = null, excludeModel = null, forcedProvider = null) {
140
+ // If a specific provider is forced, bypass normal selection
141
+ if (forcedProvider) {
142
+ // Special handling for Cline CLI - auto-install if not available
143
+ if (forcedProvider === 'cline') {
144
+ await ensureClineInstalled(true);
145
+ }
146
+
147
+ const config = await getAutoConfig();
148
+ const providerConfig = config.providers[forcedProvider];
149
+
150
+ if (!providerConfig || !providerConfig.enabled) {
151
+ return {
152
+ success: false,
153
+ error: `Forced provider ${forcedProvider} is not available or disabled`
154
+ };
155
+ }
156
+
157
+ const availableModels = providerConfig.models.filter(m =>
158
+ m.enabled && m.name !== excludeModel
159
+ );
160
+
161
+ if (availableModels.length === 0) {
162
+ return {
163
+ success: false,
164
+ error: `No available models for provider ${forcedProvider}`
165
+ };
166
+ }
167
+
168
+ return {
169
+ success: true,
170
+ provider: forcedProvider,
171
+ ide: forcedProvider,
172
+ displayName: getProviderDefinition(forcedProvider)?.displayName || forcedProvider,
173
+ model: availableModels[0].name,
174
+ config: {
175
+ ...providerConfig,
176
+ model: availableModels[0]
177
+ }
178
+ };
179
+ }
180
+
181
+ // Normal provider selection logic
182
+ return await getProviderConfig(excludeProvider);
183
+ }
184
+
185
+ /**
186
+ * Ensure Cline CLI is installed and available
187
+ */
188
+ async function ensureClineInstalled(forceInstall = false) {
189
+ const llm = new DirectLLMManager();
190
+
191
+ try {
192
+ const isAvailable = await llm.checkClineAvailability();
193
+ if (isAvailable && !forceInstall) {
194
+ return true;
195
+ }
196
+
197
+ console.log('Installing Cline CLI...');
198
+ const installResult = await llm.installCline();
199
+
200
+ if (installResult.success) {
201
+ console.log('✅ Cline CLI installed successfully');
202
+ return true;
203
+ } else {
204
+ console.error('❌ Failed to install Cline CLI:', installResult.error);
205
+ return false;
206
+ }
207
+ } catch (error) {
208
+ console.error('❌ Error installing Cline CLI:', error.message);
209
+ return false;
210
+ }
211
+ }
212
+
213
+ module.exports = {
214
+ getAllAvailableProviders,
215
+ getProviderConfig,
216
+ acquireProviderConfig,
217
+ ensureClineInstalled,
218
+ sharedProviderManager
219
+ };