mycontext-cli 4.1.4 → 4.2.0

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 (75) hide show
  1. package/README.md +231 -12
  2. package/dist/README.md +231 -12
  3. package/dist/agents/implementations/ArchitectAgent.d.ts.map +1 -1
  4. package/dist/agents/implementations/ArchitectAgent.js +9 -1
  5. package/dist/agents/implementations/ArchitectAgent.js.map +1 -1
  6. package/dist/agents/implementations/BackendDevAgent.d.ts +1 -1
  7. package/dist/agents/implementations/BackendDevAgent.d.ts.map +1 -1
  8. package/dist/agents/implementations/BackendDevAgent.js +29 -16
  9. package/dist/agents/implementations/BackendDevAgent.js.map +1 -1
  10. package/dist/agents/implementations/CodeGenSubAgent.d.ts.map +1 -1
  11. package/dist/agents/implementations/CodeGenSubAgent.js +17 -9
  12. package/dist/agents/implementations/CodeGenSubAgent.js.map +1 -1
  13. package/dist/agents/implementations/EnhancementAgent.js.map +1 -1
  14. package/dist/agents/implementations/FeatureAssemblyAgent.d.ts +1 -1
  15. package/dist/agents/implementations/FeatureAssemblyAgent.d.ts.map +1 -1
  16. package/dist/agents/implementations/FeatureAssemblyAgent.js +10 -9
  17. package/dist/agents/implementations/FeatureAssemblyAgent.js.map +1 -1
  18. package/dist/agents/implementations/PromptConstructorAgent.d.ts.map +1 -1
  19. package/dist/agents/implementations/PromptConstructorAgent.js +26 -42
  20. package/dist/agents/implementations/PromptConstructorAgent.js.map +1 -1
  21. package/dist/clients/ProviderChain.d.ts +6 -65
  22. package/dist/clients/ProviderChain.d.ts.map +1 -1
  23. package/dist/clients/ProviderChain.js +34 -194
  24. package/dist/clients/ProviderChain.js.map +1 -1
  25. package/dist/commands/agent.js +1 -1
  26. package/dist/commands/agent.js.map +1 -1
  27. package/dist/commands/assemble-features.js +1 -1
  28. package/dist/commands/assemble-features.js.map +1 -1
  29. package/dist/commands/design-analyze.js +2 -2
  30. package/dist/commands/generate-context-files.d.ts.map +1 -1
  31. package/dist/commands/generate-context-files.js +13 -0
  32. package/dist/commands/generate-context-files.js.map +1 -1
  33. package/dist/commands/generate.d.ts +1 -0
  34. package/dist/commands/generate.d.ts.map +1 -1
  35. package/dist/commands/generate.js +96 -23
  36. package/dist/commands/generate.js.map +1 -1
  37. package/dist/commands/sync-readme.js +1 -1
  38. package/dist/commands/sync-readme.js.map +1 -1
  39. package/dist/core/agents/DependencySentinel.d.ts +15 -0
  40. package/dist/core/agents/DependencySentinel.d.ts.map +1 -0
  41. package/dist/core/agents/DependencySentinel.js +141 -0
  42. package/dist/core/agents/DependencySentinel.js.map +1 -0
  43. package/dist/core/ai/AICore.d.ts +48 -0
  44. package/dist/core/ai/AICore.d.ts.map +1 -0
  45. package/dist/core/ai/AICore.js +228 -0
  46. package/dist/core/ai/AICore.js.map +1 -0
  47. package/dist/core/brain/BrainClient.d.ts +16 -0
  48. package/dist/core/brain/BrainClient.d.ts.map +1 -0
  49. package/dist/core/brain/BrainClient.js +151 -0
  50. package/dist/core/brain/BrainClient.js.map +1 -0
  51. package/dist/package.json +6 -5
  52. package/dist/types/living-context.d.ts +76 -0
  53. package/dist/types/living-context.d.ts.map +1 -0
  54. package/dist/types/living-context.js +3 -0
  55. package/dist/types/living-context.js.map +1 -0
  56. package/dist/utils/FileGenerator.d.ts +9 -0
  57. package/dist/utils/FileGenerator.d.ts.map +1 -0
  58. package/dist/utils/FileGenerator.js +106 -0
  59. package/dist/utils/FileGenerator.js.map +1 -0
  60. package/dist/utils/NextJSProjectGenerator.d.ts.map +1 -1
  61. package/dist/utils/NextJSProjectGenerator.js +59 -0
  62. package/dist/utils/NextJSProjectGenerator.js.map +1 -1
  63. package/dist/utils/contextRenderer.d.ts +12 -0
  64. package/dist/utils/contextRenderer.d.ts.map +1 -0
  65. package/dist/utils/contextRenderer.js +105 -0
  66. package/dist/utils/contextRenderer.js.map +1 -0
  67. package/dist/utils/fileSystem.d.ts +4 -0
  68. package/dist/utils/fileSystem.d.ts.map +1 -1
  69. package/dist/utils/fileSystem.js +19 -0
  70. package/dist/utils/fileSystem.js.map +1 -1
  71. package/dist/utils/hybridAIClient.d.ts +12 -117
  72. package/dist/utils/hybridAIClient.d.ts.map +1 -1
  73. package/dist/utils/hybridAIClient.js +31 -750
  74. package/dist/utils/hybridAIClient.js.map +1 -1
  75. package/package.json +6 -5
@@ -1,591 +1,52 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
2
  Object.defineProperty(exports, "__esModule", { value: true });
39
3
  exports.HybridAIClient = void 0;
40
- const claudeAgentClient_1 = require("./claudeAgentClient");
41
- const openRouterClient_1 = require("./openRouterClient");
42
- const geminiClient_1 = require("./geminiClient");
43
- const githubModelsClient_1 = require("./githubModelsClient");
44
- const XAIClient_1 = require("../clients/XAIClient");
45
- const logger_1 = require("./logger");
46
- const chalk_1 = __importDefault(require("chalk"));
47
- const fs = __importStar(require("fs"));
48
- const path = __importStar(require("path"));
49
- // Load environment variables from project files
50
- function loadEnvironmentVariables() {
51
- try {
52
- const dotenv = require("dotenv");
53
- const dotenvExpand = require("dotenv-expand");
54
- const cwd = process.cwd();
55
- const candidates = [
56
- path.join(cwd, ".mycontext", ".env.local"),
57
- path.join(cwd, ".mycontext", ".env"),
58
- path.join(cwd, ".env.local"),
59
- path.join(cwd, ".env"),
60
- ];
61
- for (const p of candidates) {
62
- if (fs.existsSync(p)) {
63
- const result = dotenv.config({ path: p, override: true });
64
- dotenvExpand.expand(result);
65
- }
66
- }
67
- }
68
- catch (err) {
69
- // Ignore errors
70
- }
71
- }
4
+ const AICore_1 = require("../core/ai/AICore");
5
+ /**
6
+ * HybridAIClient (Legacy Wrapper)
7
+ *
8
+ * This class is now a backward-compatibility layer that delegates to AICore.
9
+ * New code should use AICore.getInstance().getBestClient() directly.
10
+ */
72
11
  class HybridAIClient {
73
12
  constructor() {
74
- this.providers = [];
75
- this.currentProvider = null;
76
- this.config = null;
77
- // Load environment variables first
78
- loadEnvironmentVariables();
79
- this.loadConfig();
80
- this.initializeProviders();
81
- }
82
- /**
83
- * Load AI provider configuration
84
- */
85
- loadConfig() {
13
+ // AICore is likely already initialized by the command entry point
86
14
  try {
87
- const configPath = path.join(__dirname, "../config/ai-providers.json");
88
- const configData = fs.readFileSync(configPath, "utf8");
89
- this.config = JSON.parse(configData);
90
- }
91
- catch (error) {
92
- logger_1.logger.debug("Could not load config, using defaults");
93
- this.config = null;
94
- }
95
- }
96
- /**
97
- * Initialize available AI providers based on configuration
98
- */
99
- async initializeProviders() {
100
- // Add user API key providers first (highest priority)
101
- // Claude Agent SDK (highest priority for advanced features)
102
- // Always try ClaudeAgentClient if it has an API key (simplified approach)
103
- const claudeAgentClient = new claudeAgentClient_1.ClaudeAgentClient();
104
- if (claudeAgentClient.hasApiKey()) {
105
- // Determine provider name based on mode
106
- const providerName = claudeAgentClient.isGrokModeEnabled
107
- ? "xai"
108
- : "claude-agent";
109
- // Log the provider being used (only once)
110
- if (!HybridAIClient.hasLoggedInitialization) {
111
- if (claudeAgentClient.isGrokModeEnabled) {
112
- console.log(chalk_1.default.blue("🤖 Using Grok 4 via X AI API (direct)"));
113
- }
114
- else {
115
- console.log(chalk_1.default.blue("🎯 Using Claude Agent SDK (supports Claude, Bedrock, Vertex AI)"));
116
- }
117
- HybridAIClient.hasLoggedInitialization = true;
118
- }
119
- this.providers.push({
120
- name: providerName,
121
- priority: 0, // Highest priority for Agent SDK
122
- client: claudeAgentClient,
123
- isAvailable: () => claudeAgentClient.checkConnection(),
124
- });
125
- }
126
- // GitHub Models (High-quality GPT models)
127
- const githubClient = new githubModelsClient_1.GitHubModelsClient();
128
- if (githubClient.hasApiKey()) {
129
- if (process.env.DEBUG || process.env.VERBOSE) {
130
- console.log(chalk_1.default.gray(`[HybridAIClient] Registering GitHub Models provider`));
131
- }
132
- this.providers.push({
133
- name: "github",
134
- priority: 0.5, // Between Claude (0) and OpenRouter (1)
135
- client: githubClient,
136
- isAvailable: () => githubClient.testConnection(),
137
- });
138
- // Log if this is the only provider (or first high-priority)
139
- if (!HybridAIClient.hasLoggedInitialization) {
140
- console.log(chalk_1.default.blue("🧠 Using GitHub Models (GPT-4o) - High Quality"));
141
- HybridAIClient.hasLoggedInitialization = true;
142
- }
143
- }
144
- // OpenRouter (recommended free tier option - prioritized over XAI)
145
- const openRouterClient = new openRouterClient_1.OpenRouterClient();
146
- if (openRouterClient.hasApiKey()) {
147
- if (process.env.DEBUG || process.env.VERBOSE) {
148
- console.log(chalk_1.default.gray(`[HybridAIClient] Registering OpenRouter provider`));
149
- }
150
- this.providers.push({
151
- name: "openrouter",
152
- priority: 1, // After Claude, before Gemini/XAI
153
- client: openRouterClient,
154
- isAvailable: () => openRouterClient.checkConnection(),
155
- });
156
- // Log if this is the only provider
157
- if (!HybridAIClient.hasLoggedInitialization &&
158
- this.providers.length === 1) {
159
- console.log(chalk_1.default.blue("🧠 Using OpenRouter (DeepSeek R1) - Free Tier"));
160
- HybridAIClient.hasLoggedInitialization = true;
161
- }
162
- }
163
- else if (process.env.DEBUG || process.env.VERBOSE) {
164
- console.log(chalk_1.default.yellow(`[HybridAIClient] OpenRouter skipped: No API key found`));
165
- }
166
- // Gemini (multimodal support, visual generation)
167
- const geminiClient = new geminiClient_1.GeminiClient();
168
- if (geminiClient.hasApiKey()) {
169
- this.providers.push({
170
- name: "gemini",
171
- priority: 2, // After Claude/OpenRouter, before XAI
172
- client: geminiClient,
173
- isAvailable: async () => await geminiClient.testConnection(),
174
- });
175
- // Log if this is the only provider
176
- if (!HybridAIClient.hasLoggedInitialization &&
177
- this.providers.length === 1) {
178
- console.log(chalk_1.default.blue("✨ Using Gemini 2.0 Flash (Multimodal + Visual Generation)"));
179
- HybridAIClient.hasLoggedInitialization = true;
180
- }
181
- }
182
- // XAI/Grok (fast reasoning, multimodal support)
183
- const xaiClient = new XAIClient_1.XAIClient();
184
- if (xaiClient.hasApiKey()) {
185
- this.providers.push({
186
- name: "xai",
187
- priority: 3, // After Claude/OpenRouter/Gemini
188
- client: xaiClient,
189
- isAvailable: async () => await xaiClient.checkConnection(),
190
- });
191
- // Log if this is the only provider
192
- if (!HybridAIClient.hasLoggedInitialization &&
193
- this.providers.length === 1) {
194
- console.log(chalk_1.default.blue("✨ Using XAI Grok (Fast Reasoning)"));
195
- HybridAIClient.hasLoggedInitialization = true;
196
- }
15
+ AICore_1.AICore.getInstance();
197
16
  }
198
- // Sort by priority (lower number = higher priority)
199
- this.providers.sort((a, b) => a.priority - b.priority);
200
- logger_1.logger.verbose(`Initialized ${this.providers.length} AI providers`);
201
- if (logger_1.logger.getLogLevel() >= logger_1.LogLevel.VERBOSE) {
202
- this.providers.forEach((p) => {
203
- logger_1.logger.verbose(` • ${p.name} (priority: ${p.priority})`);
17
+ catch (e) {
18
+ AICore_1.AICore.getInstance({
19
+ workingDirectory: process.cwd(),
20
+ fallbackEnabled: true
204
21
  });
205
22
  }
206
23
  }
207
- /**
208
- * Get the best available provider
209
- */
210
- async getBestProvider(excludeProviders = new Set()) {
211
- // Optional override via env - this takes highest priority
212
- const preferredName = process.env.MYCONTEXT_PROVIDER || process.env.AI_PROVIDER || "";
213
- if (process.env.DEBUG || process.env.VERBOSE) {
214
- console.log(`[HybridAIClient] getBestProvider check: MYCONTEXT_PROVIDER=${!!process.env.MYCONTEXT_PROVIDER}, AI_PROVIDER=${!!process.env.AI_PROVIDER}`);
215
- }
216
- if (preferredName) {
217
- const preferred = this.providers.find((p) => p.name === preferredName);
218
- if (preferred) {
219
- console.log(`[HybridAIClient] Environment override: using ${preferredName}`);
220
- try {
221
- if (await preferred.isAvailable()) {
222
- this.currentProvider = preferred;
223
- return preferred;
224
- }
225
- else {
226
- console.log(`[HybridAIClient] Preferred provider ${preferred.name} not available, falling back to priority order`);
227
- }
228
- }
229
- catch (error) {
230
- console.log(`[HybridAIClient] Preferred provider ${preferred.name} not available: ${error}, falling back to priority order`);
231
- }
232
- }
233
- else {
234
- console.log(`[HybridAIClient] Preferred provider ${preferredName} not found in available providers`);
235
- }
236
- }
237
- // Choose the highest-priority available provider (excluding already-attempted ones)
238
- for (const provider of this.providers) {
239
- // Skip if this provider was already attempted
240
- if (excludeProviders.has(provider.name)) {
241
- continue;
242
- }
243
- try {
244
- const isAvailable = await provider.isAvailable();
245
- if (isAvailable) {
246
- this.currentProvider = provider;
247
- return provider;
248
- }
249
- }
250
- catch (error) {
251
- console.log(`[HybridAIClient] Provider ${provider.name} not available: ${error}`);
252
- }
253
- }
254
- return null;
24
+ get client() {
25
+ return AICore_1.AICore.getInstance().getBestClient();
255
26
  }
256
- /**
257
- * Expose current/next-best provider name for UX (e.g., spinner labels)
258
- */
259
27
  async getActiveProviderName() {
260
- const provider = await this.getBestProvider();
261
- return provider?.name ?? "unknown";
28
+ const client = this.client;
29
+ return client.providerName || "hybrid";
262
30
  }
263
- /**
264
- * Best-effort model name for generic text generation per provider
265
- */
266
31
  async getActiveTextModelName() {
267
- const provider = await this.getBestProvider();
268
- if (!provider)
269
- return "unknown";
270
- if (provider.name === "github") {
271
- // If user provided candidates via env, prefer the first
272
- const envCandidates = process.env.MYCONTEXT_MODEL_CANDIDATES;
273
- if (envCandidates) {
274
- const first = envCandidates
275
- .split(",")
276
- .map((s) => s.trim())
277
- .filter(Boolean)[0];
278
- if (first)
279
- return first;
280
- }
281
- return (this.config?.github?.models?.["component-generator"]?.name ||
282
- process.env.MYCONTEXT_MODEL ||
283
- "grok-3");
284
- }
285
- if (provider.name === "openai") {
286
- return (this.config?.openai?.models?.["text-generator"]?.name || "gpt-3.5-turbo");
287
- }
288
- if (provider.name === "claude") {
289
- return (this.config?.claude?.models?.["text-generator"]?.name ||
290
- "claude-3-5-sonnet-20241022");
291
- }
292
- if (provider.name === "huggingface") {
293
- return (this.config?.huggingface?.models?.["component-generator"]?.name ||
294
- "mycontext/react-component-generator");
295
- }
296
- if (provider.name === "gemini") {
297
- return (this.config?.gemini?.models?.["text-generator"]?.name ||
298
- "gemini-2.0-flash");
299
- }
300
- if (provider.name === "xai") {
301
- return (this.config?.xai?.models?.["text-generator"]?.name ||
302
- "grok-4-fast-reasoning");
303
- }
304
- if (provider.name === "openrouter") {
305
- return "deepseek-ai/DeepSeek-R1";
306
- }
307
- // Default model name
308
- return "qwen3-coder";
32
+ return "unified-model";
309
33
  }
310
- /**
311
- * Generate component refinement using the best available provider
312
- */
313
- async generateComponentRefinement(componentCode, prompt, options = {}) {
314
- const provider = await this.getBestProvider();
315
- if (!provider) {
316
- throw new Error("No AI providers available. Please configure Qwen API key or other providers.");
317
- }
318
- try {
319
- logger_1.logger.verbose(`Using AI provider: ${provider.name}`);
320
- let result;
321
- if (provider.name === "qwen") {
322
- const qwenClient = provider.client;
323
- result = await qwenClient.generateComponentRefinement(componentCode, prompt, options);
324
- }
325
- else if (provider.name === "github") {
326
- const githubClient = provider.client;
327
- result = await githubClient.generateComponentRefinement(componentCode, prompt, options);
328
- }
329
- else if (provider.name === "openai") {
330
- const openaiClient = provider.client;
331
- result = await openaiClient.generateComponentRefinement(componentCode, prompt, options);
332
- }
333
- else if (provider.name === "claude") {
334
- const claudeClient = provider.client;
335
- result = await claudeClient.generateComponentRefinement(componentCode, prompt, options);
336
- }
337
- else if (provider.name === "huggingface") {
338
- const huggingFaceClient = provider.client;
339
- result = await huggingFaceClient.generateComponentRefinement(componentCode, prompt, options);
340
- }
341
- else if (provider.name === "gemini") {
342
- const geminiClient = provider.client;
343
- const response = await geminiClient.generateText(`Refine this React component: ${componentCode}\n\nUser request: ${prompt}`, options);
344
- result = response.text;
345
- }
346
- else if (provider.name === "xai") {
347
- const xaiClient = provider.client;
348
- const modelName = await this.getActiveTextModelName();
349
- result = await xaiClient.generateComponentRefinement(componentCode, prompt, { ...options, model: modelName });
350
- }
351
- else {
352
- // No fallback - fail cleanly
353
- throw new Error("AI generation failed - no fallbacks allowed");
354
- }
355
- // Extract code and explanation
356
- const { code, explanation } = this.parseAIResponse(result);
357
- return {
358
- code,
359
- provider: provider.name,
360
- explanation,
361
- };
362
- }
363
- catch (error) {
364
- console.log(`[HybridAIClient] Provider ${provider.name} failed: ${error.message}`);
365
- // Try next provider
366
- const nextProvider = this.providers.find((p) => p.priority > provider.priority);
367
- if (nextProvider) {
368
- console.log(`[HybridAIClient] Trying next provider: ${nextProvider.name}`);
369
- return this.generateComponentRefinement(componentCode, prompt, options);
370
- }
371
- throw error;
372
- }
34
+ async generateText(prompt, options = {}) {
35
+ const text = await this.client.generateText(prompt, options);
36
+ return { text, provider: await this.getActiveProviderName() };
373
37
  }
374
- /**
375
- * Generate new component using the best available provider
376
- */
377
38
  async generateComponent(prompt, options = {}) {
378
- return this.tryGenerateWithProviders(prompt, options, []);
39
+ const result = await this.client.generateComponent(prompt, undefined, options);
40
+ const { code, explanation } = this.parseAIResponse(result);
41
+ return { code, provider: await this.getActiveProviderName(), explanation };
379
42
  }
380
- async tryGenerateWithProviders(prompt, options, failedProviders = [], maxRetries = 3) {
381
- // Find next available provider not in failed list
382
- for (const provider of this.providers) {
383
- if (failedProviders.includes(provider.name)) {
384
- continue;
385
- }
386
- try {
387
- logger_1.logger.verbose(`Using AI provider: ${provider.name}`);
388
- let result;
389
- if (provider.name === "qwen") {
390
- const qwenClient = provider.client;
391
- result = await qwenClient.generateComponent(prompt, options);
392
- }
393
- else if (provider.name === "github") {
394
- const githubClient = provider.client;
395
- result = await githubClient.generateComponent(prompt, options);
396
- }
397
- else if (provider.name === "openai") {
398
- const openaiClient = provider.client;
399
- result = await openaiClient.generateComponent(prompt, options);
400
- }
401
- else if (provider.name === "claude") {
402
- const claudeClient = provider.client;
403
- result = await claudeClient.generateComponent(prompt, options);
404
- }
405
- else if (provider.name === "huggingface") {
406
- const huggingFaceClient = provider.client;
407
- result = await huggingFaceClient.generateComponent(prompt, options);
408
- }
409
- else if (provider.name === "gemini") {
410
- const geminiClient = provider.client;
411
- const response = await geminiClient.generateComponent(prompt, options);
412
- result = response.code;
413
- }
414
- else if (provider.name === "xai") {
415
- const xaiClient = provider.client;
416
- const modelName = await this.getActiveTextModelName();
417
- result = await xaiClient.generateComponent(prompt, {
418
- ...options,
419
- model: modelName,
420
- });
421
- }
422
- else if (provider.name === "hosted") {
423
- const hostedClient = provider.client;
424
- const response = await hostedClient.generateComponent(prompt, options);
425
- if (response.success && response.content) {
426
- result = response.content;
427
- }
428
- else {
429
- throw new Error(response.error || "Hosted API generation failed");
430
- }
431
- }
432
- else {
433
- // No fallback - fail cleanly
434
- throw new Error("AI generation failed - no fallbacks allowed");
435
- }
436
- // Extract code and explanation
437
- const { code, explanation } = this.parseAIResponse(result);
438
- return {
439
- code,
440
- provider: provider.name,
441
- explanation,
442
- };
443
- }
444
- catch (error) {
445
- console.log(`[HybridAIClient] Provider ${provider.name} failed: ${error.message}`);
446
- // Add to failed list and continue to next provider
447
- failedProviders.push(provider.name);
448
- // If this is a 402 Payment Required error, don't retry the same provider
449
- if (error.message.includes("402") ||
450
- error.message.includes("Payment Required")) {
451
- console.log(`[HybridAIClient] Skipping ${provider.name} due to payment/credit issues`);
452
- continue;
453
- }
454
- // If this is a rate limit error, add to failed list but don't give up immediately
455
- if (error.message.includes("429") ||
456
- error.message.includes("Rate limit") ||
457
- error.message.includes("RateLimitReached")) {
458
- console.log(`[HybridAIClient] ${provider.name} rate limited, will try other providers first`);
459
- // Don't add to failed list immediately - try other providers first
460
- continue;
461
- }
462
- }
463
- }
464
- // All providers failed
465
- const failedList = failedProviders.join(", ");
466
- console.log(chalk_1.default.red("❌ All AI providers failed"));
467
- console.log(chalk_1.default.yellow("💡 All AI providers failed. Retry options:"));
468
- console.log(chalk_1.default.gray(" 1. Wait for rate limits to reset"));
469
- console.log(chalk_1.default.gray(" 2. Check API key configuration"));
470
- console.log(chalk_1.default.gray(" 3. Try again later"));
471
- throw new Error(`All AI providers failed: ${failedList}. Retry when conditions improve.`);
472
- }
473
- /**
474
- * Generate generic text (used for PRD, types, brand, etc.)
475
- */
476
- async generateText(prompt, options = {}, attemptedProviders = new Set()) {
477
- const spinnerCallback = options.spinnerCallback;
478
- const provider = await this.getBestProvider(attemptedProviders);
479
- const timeout = options.timeout || 180000; // 3 minute timeout for reasoning models like DeepSeek R1
480
- if (!provider) {
481
- // No providers available - fail cleanly
482
- console.log(chalk_1.default.red("❌ No AI providers available"));
483
- console.log(chalk_1.default.yellow("💡 Configure API keys and retry"));
484
- throw new Error("No AI providers available - configure API keys and retry");
485
- }
486
- // Check if we've already attempted this provider (prevent infinite loop)
487
- if (attemptedProviders.has(provider.name)) {
488
- console.log(chalk_1.default.red(`❌ Already attempted provider: ${provider.name}`));
489
- throw new Error(`All AI providers exhausted. Attempted: ${Array.from(attemptedProviders).join(", ")}`);
490
- }
491
- // Safety check: maximum attempts
492
- if (attemptedProviders.size >= this.providers.length) {
493
- console.log(chalk_1.default.red(`❌ Maximum provider attempts reached (${attemptedProviders.size}/${this.providers.length})`));
494
- throw new Error(`All ${this.providers.length} AI providers failed. Attempted: ${Array.from(attemptedProviders).join(", ")}`);
495
- }
496
- try {
497
- let text;
498
- // Wrap each provider call with timeout
499
- const providerCall = async () => {
500
- if (provider.name === "qwen") {
501
- const qwenClient = provider.client;
502
- return await qwenClient.generateText(prompt, options);
503
- }
504
- else if (provider.name === "github") {
505
- const githubClient = provider.client;
506
- return await githubClient.generateText(prompt, options);
507
- }
508
- else if (provider.name === "openai") {
509
- const openaiClient = provider.client;
510
- return await openaiClient.generateText(prompt, options);
511
- }
512
- else if (provider.name === "claude") {
513
- const claudeClient = provider.client;
514
- return await claudeClient.generateText(prompt, options);
515
- }
516
- else if (provider.name === "huggingface") {
517
- const hfClient = provider.client;
518
- return await hfClient.generateComponent(prompt, options);
519
- }
520
- else if (provider.name === "gemini") {
521
- const geminiClient = provider.client;
522
- const response = await geminiClient.generateText(prompt, options);
523
- return response.text;
524
- }
525
- else if (provider.name === "xai") {
526
- const xaiClient = provider.client;
527
- const modelName = await this.getActiveTextModelName();
528
- return await xaiClient.generateText(prompt, {
529
- ...options,
530
- model: modelName,
531
- });
532
- }
533
- else if (provider.name === "openrouter") {
534
- const openRouterClient = provider.client;
535
- return await openRouterClient.generateText(prompt, options);
536
- }
537
- else if (provider.name === "hosted") {
538
- const hostedClient = provider.client;
539
- const response = await hostedClient.generateText(prompt, options);
540
- if (response.success && response.content) {
541
- return response.content;
542
- }
543
- else {
544
- throw new Error(response.error || "Hosted API generation failed");
545
- }
546
- }
547
- else {
548
- // No fallback - fail cleanly
549
- throw new Error("AI generation failed - no fallbacks allowed");
550
- }
551
- };
552
- // Execute with timeout
553
- text = await Promise.race([
554
- providerCall(),
555
- new Promise((_, reject) => setTimeout(() => reject(new Error(`Timeout after ${timeout}ms`)), timeout)),
556
- ]);
557
- return { text, provider: provider.name };
558
- }
559
- catch (error) {
560
- console.log(chalk_1.default.yellow(`[HybridAIClient] Provider ${provider.name} failed: ${error.message}`));
561
- // Mark this provider as attempted
562
- attemptedProviders.add(provider.name);
563
- // Check for remaining providers
564
- const remainingProviders = this.providers
565
- .filter(p => !attemptedProviders.has(p.name))
566
- .map(p => p.name);
567
- if (remainingProviders.length > 0) {
568
- console.log(chalk_1.default.blue(`[HybridAIClient] Retrying with next available provider: ${remainingProviders[0]} (remaining: ${remainingProviders.join(", ")})`));
569
- return this.generateText(prompt, options, attemptedProviders);
570
- }
571
- else {
572
- console.log(chalk_1.default.red(`[HybridAIClient] No more AI providers available (attempted: ${Array.from(attemptedProviders).join(", ")})`));
573
- throw error;
574
- }
575
- }
43
+ async generateComponentRefinement(componentCode, prompt, options = {}) {
44
+ const result = await this.client.generateComponentRefinement(componentCode, prompt, undefined, options);
45
+ const { code, explanation } = this.parseAIResponse(result);
46
+ return { code, provider: await this.getActiveProviderName(), explanation };
576
47
  }
577
- /**
578
- * Parse AI response to separate code from explanation
579
- * Enhanced with better pattern matching and truncation handling
580
- */
581
48
  parseAIResponse(response) {
582
- console.log(`🔍 DEBUG: Parsing AI response (length: ${response.length})`);
583
- console.log(`🔍 DEBUG: Response preview: ${response.substring(0, 200)}...`);
584
- // Check if response is truncated (common issue)
585
- if (response.length < 100) {
586
- console.warn(`⚠️ WARNING: Response appears truncated (${response.length} chars)`);
587
- }
588
- // Enhanced code block detection - look for any code block markers
49
+ // Reuse the parsing logic from old client for compatibility
589
50
  const codeBlockPatterns = [
590
51
  /```(?:tsx|jsx|ts|js|typescript|javascript)?\s*\n([\s\S]*?)```/g,
591
52
  /```(?:tsx|jsx|ts|js|typescript|javascript)?\s*([\s\S]*?)```/g,
@@ -595,194 +56,14 @@ class HybridAIClient {
595
56
  for (const pattern of codeBlockPatterns) {
596
57
  const matches = [...response.matchAll(pattern)];
597
58
  if (matches.length > 0) {
598
- // Use the longest match (most complete code)
599
59
  const longestMatch = matches.reduce((longest, match) => (match[1]?.length || 0) > (longest[1]?.length || 0) ? match : longest);
600
60
  const code = longestMatch[1]?.trim() || "";
601
61
  const explanation = response.replace(longestMatch[0], "").trim();
602
- console.log(`✅ DEBUG: Found code block (${code.length} chars)`);
603
- return {
604
- code,
605
- explanation: explanation || undefined,
606
- };
607
- }
608
- }
609
- // Enhanced JSX/TSX content detection
610
- const jsxPatterns = [
611
- // Complete component with imports
612
- /(?:import\s+.*?;\s*)*\s*(?:export\s+)?(?:function|const)\s+\w+.*?{[\s\S]*?}(?:\s*export\s+default\s+\w+;?)?/g,
613
- // Function/const component
614
- /(?:export\s+)?(?:function|const)\s+\w+.*?{[\s\S]*?}/g,
615
- // Class component
616
- /(?:export\s+)?class\s+\w+.*?{[\s\S]*?}/g,
617
- // Interface/type definitions
618
- /(?:export\s+)?(?:interface|type)\s+\w+.*?{[\s\S]*?}/g,
619
- ];
620
- for (const pattern of jsxPatterns) {
621
- const matches = [...response.matchAll(pattern)];
622
- if (matches.length > 0) {
623
- // Use the longest match (most complete code)
624
- const longestMatch = matches.reduce((longest, match) => match[0].length > longest[0].length ? match : longest);
625
- const code = longestMatch[0].trim();
626
- const explanation = response.replace(longestMatch[0], "").trim();
627
- console.log(`✅ DEBUG: Found JSX/TSX content (${code.length} chars)`);
628
- return {
629
- code,
630
- explanation: explanation || undefined,
631
- };
632
- }
633
- }
634
- // Look for incomplete code that might be truncated
635
- const incompletePatterns = [
636
- // Incomplete function/const
637
- /(?:export\s+)?(?:function|const)\s+\w+.*?{[\s\S]*$/,
638
- // Incomplete import statements
639
- /import\s+.*?$/,
640
- // Incomplete interface/type
641
- /(?:export\s+)?(?:interface|type)\s+\w+.*?{[\s\S]*$/,
642
- ];
643
- for (const pattern of incompletePatterns) {
644
- const match = response.match(pattern);
645
- if (match) {
646
- console.warn(`⚠️ WARNING: Found incomplete code pattern (${match[0].length} chars)`);
647
- console.warn(`⚠️ WARNING: This suggests the response was truncated`);
648
- // Try to complete the incomplete code
649
- let code = match[0].trim();
650
- // If it ends with an incomplete function, try to close it
651
- if (code.includes("{") && !code.includes("}")) {
652
- code +=
653
- "\n // TODO: Complete this component - response was truncated\n return <div>Incomplete component</div>;\n}";
654
- }
655
- return {
656
- code,
657
- explanation: "⚠️ WARNING: Response appears to be truncated. Please retry with a different model or increase token limits.",
658
- };
659
- }
660
- }
661
- // If no patterns match, return the whole response as code
662
- console.log(`⚠️ WARNING: No code patterns found, returning full response as code`);
663
- return { code: response };
664
- }
665
- /**
666
- * Get provider status
667
- */
668
- async getProviderStatus() {
669
- const status = [];
670
- for (const provider of this.providers) {
671
- try {
672
- const available = await provider.isAvailable();
673
- status.push({
674
- name: provider.name,
675
- available,
676
- priority: provider.priority,
677
- });
62
+ return { code, explanation: explanation || undefined };
678
63
  }
679
- catch (error) {
680
- status.push({
681
- name: provider.name,
682
- available: false,
683
- priority: provider.priority,
684
- });
685
- }
686
- }
687
- return status;
688
- }
689
- /**
690
- * Set Hugging Face API key
691
- */
692
- setHuggingFaceApiKey(apiKey) {
693
- const huggingFaceProvider = this.providers.find((p) => p.name === "huggingface");
694
- if (huggingFaceProvider) {
695
- huggingFaceProvider.client.setApiKey(apiKey);
696
- }
697
- }
698
- /**
699
- * Set OpenAI API key
700
- */
701
- setOpenAIApiKey(apiKey) {
702
- const openaiProvider = this.providers.find((p) => p.name === "openai");
703
- if (openaiProvider) {
704
- openaiProvider.client.setApiKey(apiKey);
705
- }
706
- }
707
- /**
708
- * Set Claude API key
709
- */
710
- setClaudeApiKey(apiKey) {
711
- const claudeProvider = this.providers.find((p) => p.name === "claude");
712
- if (claudeProvider) {
713
- claudeProvider.client.setApiKey(apiKey);
714
- }
715
- }
716
- /**
717
- * Set Gemini API key
718
- */
719
- setGeminiApiKey(apiKey) {
720
- const geminiProvider = this.providers.find((p) => p.name === "gemini");
721
- if (geminiProvider) {
722
- geminiProvider.client.setApiKey(apiKey);
723
- }
724
- }
725
- async getAvailableProviders() {
726
- return this.providers;
727
- }
728
- async getAllProviders() {
729
- return this.providers;
730
- }
731
- /**
732
- * Set Claude Agent API key
733
- */
734
- setClaudeAgentApiKey(apiKey) {
735
- const claudeAgentProvider = this.providers.find((p) => p.name === "claude-agent");
736
- if (claudeAgentProvider) {
737
- claudeAgentProvider.client.setApiKey(apiKey);
738
64
  }
739
- }
740
- /**
741
- * Generate component using Claude Agent SDK with enhanced context
742
- */
743
- async generateComponentWithAgent(prompt, context = {}, options = {}) {
744
- const claudeAgentProvider = this.providers.find((p) => p.name === "claude-agent");
745
- if (claudeAgentProvider && (await claudeAgentProvider.isAvailable())) {
746
- const agentClient = claudeAgentProvider.client;
747
- return await agentClient.generateComponent(prompt, context, options);
748
- }
749
- // Fallback to regular component generation
750
- const result = await this.generateComponent(prompt, options);
751
- return typeof result === "string" ? result : result.code;
752
- }
753
- /**
754
- * Run agent workflow with Claude Agent SDK
755
- */
756
- async runAgentWorkflow(workflowPrompt, context = {}, options = {}) {
757
- const claudeAgentProvider = this.providers.find((p) => p.name === "claude-agent");
758
- if (claudeAgentProvider && (await claudeAgentProvider.isAvailable())) {
759
- const agentClient = claudeAgentProvider.client;
760
- return await agentClient.runAgentWorkflow(workflowPrompt, context, options);
761
- }
762
- // Fallback to regular text generation
763
- const result = await this.generateText(workflowPrompt, options);
764
- const content = typeof result === "string" ? result : result.text;
765
- return { content, context };
766
- }
767
- /**
768
- * Check if Claude Agent SDK is available
769
- */
770
- async isClaudeAgentAvailable() {
771
- const claudeAgentProvider = this.providers.find((p) => p.name === "claude-agent");
772
- return claudeAgentProvider
773
- ? await claudeAgentProvider.isAvailable()
774
- : false;
775
- }
776
- /**
777
- * Get Claude Agent SDK client if available
778
- */
779
- getClaudeAgentClient() {
780
- const claudeAgentProvider = this.providers.find((p) => p.name === "claude-agent");
781
- return claudeAgentProvider
782
- ? claudeAgentProvider.client
783
- : null;
65
+ return { code: response.trim() };
784
66
  }
785
67
  }
786
68
  exports.HybridAIClient = HybridAIClient;
787
- HybridAIClient.hasLoggedInitialization = false;
788
69
  //# sourceMappingURL=hybridAIClient.js.map