mycontext-cli 0.4.19 → 1.0.1
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/README.md +14 -11
- package/dist/agents/implementations/BackendDevAgent.d.ts +39 -0
- package/dist/agents/implementations/BackendDevAgent.d.ts.map +1 -0
- package/dist/agents/implementations/BackendDevAgent.js +821 -0
- package/dist/agents/implementations/BackendDevAgent.js.map +1 -0
- package/dist/agents/implementations/CodeGenSubAgent.d.ts +1 -1
- package/dist/agents/implementations/CodeGenSubAgent.d.ts.map +1 -1
- package/dist/agents/implementations/CodeGenSubAgent.js +34 -22
- package/dist/agents/implementations/CodeGenSubAgent.js.map +1 -1
- package/dist/agents/implementations/DocsSubAgent.d.ts +7 -0
- package/dist/agents/implementations/DocsSubAgent.d.ts.map +1 -1
- package/dist/agents/implementations/DocsSubAgent.js +20 -11
- package/dist/agents/implementations/DocsSubAgent.js.map +1 -1
- package/dist/agents/implementations/EnhancementAgent.d.ts +1 -1
- package/dist/agents/implementations/EnhancementAgent.d.ts.map +1 -1
- package/dist/agents/implementations/EnhancementAgent.js +17 -12
- package/dist/agents/implementations/EnhancementAgent.js.map +1 -1
- package/dist/cli.js +191 -39
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts +52 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +740 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/auth.d.ts +19 -26
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +187 -180
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/build-app.d.ts +33 -0
- package/dist/commands/build-app.d.ts.map +1 -0
- package/dist/commands/build-app.js +507 -0
- package/dist/commands/build-app.js.map +1 -0
- package/dist/commands/generate-components.d.ts +2 -0
- package/dist/commands/generate-components.d.ts.map +1 -1
- package/dist/commands/generate-components.js +95 -49
- package/dist/commands/generate-components.js.map +1 -1
- package/dist/commands/generate.d.ts +3 -0
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +250 -93
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +86 -7
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +10 -7
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/migrate.d.ts +29 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +485 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/model.d.ts +13 -16
- package/dist/commands/model.d.ts.map +1 -1
- package/dist/commands/model.js +86 -320
- package/dist/commands/model.js.map +1 -1
- package/dist/commands/playbooks.d.ts +33 -0
- package/dist/commands/playbooks.d.ts.map +1 -0
- package/dist/commands/playbooks.js +662 -0
- package/dist/commands/playbooks.js.map +1 -0
- package/dist/commands/pricing.d.ts +15 -0
- package/dist/commands/pricing.d.ts.map +1 -0
- package/dist/commands/pricing.js +129 -0
- package/dist/commands/pricing.js.map +1 -0
- package/dist/commands/promote.d.ts +22 -0
- package/dist/commands/promote.d.ts.map +1 -0
- package/dist/commands/promote.js +204 -0
- package/dist/commands/promote.js.map +1 -0
- package/dist/commands/setup.d.ts +5 -18
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +180 -340
- package/dist/commands/setup.js.map +1 -1
- package/dist/config/ai-providers.json +37 -28
- package/dist/config/api.d.ts +9 -0
- package/dist/config/api.d.ts.map +1 -0
- package/dist/config/api.js +25 -0
- package/dist/config/api.js.map +1 -0
- package/dist/config/api.ts +29 -0
- package/dist/config/pricing.json +55 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/apiKeyManager.d.ts.map +1 -1
- package/dist/utils/apiKeyManager.js +7 -0
- package/dist/utils/apiKeyManager.js.map +1 -1
- package/dist/utils/fileSystem.d.ts.map +1 -1
- package/dist/utils/fileSystem.js +3 -1
- package/dist/utils/fileSystem.js.map +1 -1
- package/dist/utils/hostedApiClient.d.ts +32 -0
- package/dist/utils/hostedApiClient.d.ts.map +1 -0
- package/dist/utils/hostedApiClient.js +231 -0
- package/dist/utils/hostedApiClient.js.map +1 -0
- package/dist/utils/hybridAIClient.d.ts +7 -3
- package/dist/utils/hybridAIClient.d.ts.map +1 -1
- package/dist/utils/hybridAIClient.js +103 -27
- package/dist/utils/hybridAIClient.js.map +1 -1
- package/dist/utils/qwenClient.d.ts +22 -0
- package/dist/utils/qwenClient.d.ts.map +1 -0
- package/dist/utils/qwenClient.js +180 -0
- package/dist/utils/qwenClient.js.map +1 -0
- package/package.json +1 -1
|
@@ -44,16 +44,18 @@ const spinner_1 = require("../utils/spinner");
|
|
|
44
44
|
const fileSystem_1 = require("../utils/fileSystem");
|
|
45
45
|
const hybridAIClient_1 = require("../utils/hybridAIClient");
|
|
46
46
|
const githubModelsClient_1 = require("../utils/githubModelsClient");
|
|
47
|
+
const hostedApiClient_1 = require("../utils/hostedApiClient");
|
|
47
48
|
const fs = __importStar(require("fs-extra"));
|
|
48
49
|
class GenerateCommand {
|
|
49
50
|
constructor() {
|
|
50
51
|
this.fs = new fileSystem_1.FileSystemManager();
|
|
51
52
|
this.spinner = new spinner_1.EnhancedSpinner("Processing...");
|
|
52
53
|
this.ai = new hybridAIClient_1.HybridAIClient();
|
|
54
|
+
this.hostedApi = new hostedApiClient_1.HostedApiClient();
|
|
53
55
|
}
|
|
54
56
|
async execute(options) {
|
|
55
57
|
try {
|
|
56
|
-
this.spinner.start();
|
|
58
|
+
this.spinner.start().updateText("Analyzing project context...");
|
|
57
59
|
const projectContext = await this.resolveInputContext(options);
|
|
58
60
|
if (!projectContext) {
|
|
59
61
|
throw new Error("No project context found. Run 'mycontext init' or pass --description/--context-file.");
|
|
@@ -146,14 +148,16 @@ class GenerateCommand {
|
|
|
146
148
|
else {
|
|
147
149
|
console.log(chalk_1.default.yellow(`⚠️ Project structure generation skipped or failed: ${structureRes.error || "unknown"}`));
|
|
148
150
|
}
|
|
149
|
-
this.spinner.success({
|
|
151
|
+
this.spinner.success({
|
|
152
|
+
text: "🎉 All artifacts generated successfully!",
|
|
153
|
+
});
|
|
150
154
|
return;
|
|
151
155
|
}
|
|
152
156
|
default:
|
|
153
157
|
throw new Error(`Unknown generation type: ${type}`);
|
|
154
158
|
}
|
|
155
159
|
if (result.success && result.content) {
|
|
156
|
-
this.spinner.success({ text:
|
|
160
|
+
this.spinner.success({ text: `✅ ${type} generated successfully!` });
|
|
157
161
|
// Save the generated content
|
|
158
162
|
const outputPath = await this.saveGeneratedContent(type, result.content, options);
|
|
159
163
|
console.log(chalk_1.default.green(`✅ Generated ${type} saved to: ${outputPath}`));
|
|
@@ -245,15 +249,24 @@ class GenerateCommand {
|
|
|
245
249
|
// Friendlier handling for missing providers
|
|
246
250
|
const message = error instanceof Error ? error.message : String(error);
|
|
247
251
|
if (/No AI providers available/i.test(message)) {
|
|
248
|
-
console.log(chalk_1.default.yellow("⚠️ No AI providers configured.
|
|
249
|
-
console.log(chalk_1.default.
|
|
252
|
+
console.log(chalk_1.default.yellow("\n⚠️ No AI providers configured. Choose your option:"));
|
|
253
|
+
console.log(chalk_1.default.blue("\n🔑 Option 1: Use your own AI keys (recommended)"));
|
|
254
|
+
console.log(chalk_1.default.gray(" 1. Copy: .mycontext/.env.example → .mycontext/.env"));
|
|
255
|
+
console.log(chalk_1.default.gray(" 2. Add: MYCONTEXT_GITHUB_TOKEN=ghp_xxx"));
|
|
256
|
+
console.log(chalk_1.default.gray(" 3. Or: MYCONTEXT_QWEN_API_KEY=sk-or-xxx (free via OpenRouter)"));
|
|
257
|
+
console.log(chalk_1.default.blue("\n🌐 Option 2: Use hosted MyContext AI"));
|
|
258
|
+
console.log(chalk_1.default.gray(" 1. Run: mycontext auth login"));
|
|
259
|
+
console.log(chalk_1.default.gray(" 2. Sign up for free at: https://mycontext.fbien.com"));
|
|
260
|
+
console.log(chalk_1.default.gray(" 3. Get $5/month unlimited plan"));
|
|
250
261
|
// Fallback: write a minimal, valid skeleton so users can proceed
|
|
251
262
|
try {
|
|
252
263
|
const fallbackType = options?.type || "context";
|
|
253
264
|
const fallbackProjectContext = (await this.getProjectContext()) || {};
|
|
254
265
|
const skeleton = this.buildSkeleton(String(fallbackType), fallbackProjectContext);
|
|
255
266
|
const outputPath = await this.saveGeneratedContent(String(fallbackType), skeleton, options);
|
|
256
|
-
this.spinner.success({
|
|
267
|
+
this.spinner.success({
|
|
268
|
+
text: `✨ Generated basic template → ${outputPath}`,
|
|
269
|
+
});
|
|
257
270
|
return;
|
|
258
271
|
}
|
|
259
272
|
catch (e) {
|
|
@@ -277,6 +290,18 @@ class GenerateCommand {
|
|
|
277
290
|
return null;
|
|
278
291
|
}
|
|
279
292
|
}
|
|
293
|
+
async getActivePlaybook() {
|
|
294
|
+
try {
|
|
295
|
+
const playbookPath = path_1.default.join(process.cwd(), ".mycontext", "active-playbook.json");
|
|
296
|
+
if (await fs.pathExists(playbookPath)) {
|
|
297
|
+
return await fs.readJson(playbookPath);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
// Ignore errors
|
|
302
|
+
}
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
280
305
|
async resolveInputContext(options) {
|
|
281
306
|
// 1) Explicit description wins
|
|
282
307
|
if (options.description && options.description.trim()) {
|
|
@@ -336,35 +361,31 @@ class GenerateCommand {
|
|
|
336
361
|
catch { }
|
|
337
362
|
// 5) Interactive prompt to capture description (when not in --yes)
|
|
338
363
|
if (!options.yes) {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
if (pasted && pasted.trim().length > 0) {
|
|
351
|
-
const desc = pasted.trim();
|
|
352
|
-
// Truncate long descriptions for terminal display
|
|
353
|
-
const truncatedDesc = desc.length > 100 ? desc.substring(0, 100) + "..." : desc;
|
|
354
|
-
console.log(chalk_1.default.gray(`📝 Interactive description: ${truncatedDesc}`));
|
|
355
|
-
return { description: desc };
|
|
356
|
-
}
|
|
364
|
+
this.spinner.stop();
|
|
365
|
+
console.log(chalk_1.default.blue("\n📝 Project Description"));
|
|
366
|
+
console.log(chalk_1.default.gray("Enter your project description or PRD. Press Ctrl-D when finished."));
|
|
367
|
+
const pasted = await this.readStdinBlockUntilEOF();
|
|
368
|
+
if (pasted && pasted.trim().length > 0) {
|
|
369
|
+
const desc = pasted.trim();
|
|
370
|
+
// Truncate long descriptions for terminal display
|
|
371
|
+
const truncatedDesc = desc.length > 100 ? desc.substring(0, 100) + "..." : desc;
|
|
372
|
+
console.log(chalk_1.default.gray(`📝 Description: ${truncatedDesc}`));
|
|
373
|
+
this.spinner.start().updateText("Processing description...");
|
|
374
|
+
return { description: desc };
|
|
357
375
|
}
|
|
358
376
|
else {
|
|
377
|
+
// Fallback to simple text input
|
|
359
378
|
const short = await (0, prompts_1.default)({
|
|
360
379
|
type: "text",
|
|
361
380
|
name: "desc",
|
|
362
|
-
message: "Short project description
|
|
381
|
+
message: "Short project description:",
|
|
363
382
|
});
|
|
364
383
|
if (short?.desc && String(short.desc).trim().length > 0) {
|
|
384
|
+
this.spinner.start().updateText("Processing description...");
|
|
365
385
|
return { description: String(short.desc).trim() };
|
|
366
386
|
}
|
|
367
387
|
}
|
|
388
|
+
this.spinner.start().updateText("Continuing without description...");
|
|
368
389
|
}
|
|
369
390
|
// 6) Fallback to project config
|
|
370
391
|
return await this.getProjectContext();
|
|
@@ -391,10 +412,29 @@ class GenerateCommand {
|
|
|
391
412
|
}
|
|
392
413
|
}
|
|
393
414
|
catch { }
|
|
415
|
+
// Get active playbook for additional context
|
|
416
|
+
const activePlaybook = await this.getActivePlaybook();
|
|
394
417
|
const prompt = [
|
|
395
418
|
`[mycontext] Plan: plan → generate → QA → docs → preview (→ checks)`,
|
|
396
419
|
`Create a production-grade PRD for: ${projectContext.description || "MyContext project"}`,
|
|
397
420
|
"",
|
|
421
|
+
// Include playbook context if available
|
|
422
|
+
...(activePlaybook
|
|
423
|
+
? [
|
|
424
|
+
"",
|
|
425
|
+
"## Proven Process Reference",
|
|
426
|
+
`**Active Playbook: ${activePlaybook.title}**`,
|
|
427
|
+
`Category: ${activePlaybook.category || "N/A"}`,
|
|
428
|
+
`Difficulty: ${activePlaybook.metadata?.difficulty || "N/A"}`,
|
|
429
|
+
`Estimated Time: ${activePlaybook.metadata?.estimatedTime || "N/A"}`,
|
|
430
|
+
"",
|
|
431
|
+
"**Process Guidelines:**",
|
|
432
|
+
activePlaybook.content,
|
|
433
|
+
"",
|
|
434
|
+
"**Important:** Follow the proven patterns and best practices outlined in the playbook above. This will ensure your implementation follows industry standards and proven approaches.",
|
|
435
|
+
"",
|
|
436
|
+
]
|
|
437
|
+
: []),
|
|
398
438
|
"Assume default stack unless user specifies otherwise:",
|
|
399
439
|
"- Frontend: Next.js (App Router) with Shadcn UI",
|
|
400
440
|
"- Auth/DB/File storage: InstantDB (auth, data, file persistence)",
|
|
@@ -455,22 +495,51 @@ class GenerateCommand {
|
|
|
455
495
|
"Write concretely. Ground content in the provided description. No placeholders.",
|
|
456
496
|
].join("\n");
|
|
457
497
|
try {
|
|
458
|
-
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
498
|
+
// Check if user has local AI keys configured
|
|
499
|
+
const hasLocalKeys = this.hasLocalAIKeys();
|
|
500
|
+
if (hasLocalKeys) {
|
|
501
|
+
// Use local AI first (user's own keys)
|
|
502
|
+
this.spinner.updateText(`🤖 Generating PRD with ${await this.ai.getActiveProviderName()}...`);
|
|
503
|
+
const { text, provider } = await this.ai.generateText(prompt, {
|
|
504
|
+
model: options.model || process.env.MYCONTEXT_MODEL,
|
|
505
|
+
modelCandidates: this.getModelCandidates(options),
|
|
506
|
+
});
|
|
507
|
+
const cleaned = this.sanitizeMarkdownOutput(text);
|
|
508
|
+
return {
|
|
509
|
+
success: true,
|
|
510
|
+
content: cleaned,
|
|
511
|
+
provider: provider,
|
|
512
|
+
metadata: {
|
|
513
|
+
model: "local",
|
|
514
|
+
tokens: cleaned.length / 4,
|
|
515
|
+
latency: 1000,
|
|
516
|
+
},
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
// No local keys - use hosted API (requires authentication)
|
|
521
|
+
this.spinner.updateText("🤖 Generating PRD with MyContext AI (hosted)...");
|
|
522
|
+
const hostedResult = await this.hostedApi.generateContext("context", prompt, {
|
|
523
|
+
model: options.model || "mycontext",
|
|
524
|
+
context: projectContext,
|
|
525
|
+
});
|
|
526
|
+
if (hostedResult.success && hostedResult.data) {
|
|
527
|
+
const cleaned = this.sanitizeMarkdownOutput(hostedResult.data);
|
|
528
|
+
return {
|
|
529
|
+
success: true,
|
|
530
|
+
content: cleaned,
|
|
531
|
+
provider: "hosted",
|
|
532
|
+
metadata: {
|
|
533
|
+
model: "mycontext",
|
|
534
|
+
tokens: cleaned.length / 4,
|
|
535
|
+
latency: 1000,
|
|
536
|
+
},
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
throw new Error(hostedResult.error || "Hosted API failed");
|
|
541
|
+
}
|
|
542
|
+
}
|
|
474
543
|
}
|
|
475
544
|
catch (error) {
|
|
476
545
|
return {
|
|
@@ -536,23 +605,53 @@ lib/types/
|
|
|
536
605
|
- Use generics where appropriate
|
|
537
606
|
- Keep types focused and single-purpose`;
|
|
538
607
|
try {
|
|
539
|
-
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
content
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
608
|
+
// Check if user has local AI keys configured
|
|
609
|
+
const hasLocalKeys = this.hasLocalAIKeys();
|
|
610
|
+
if (hasLocalKeys) {
|
|
611
|
+
// Use local AI first (user's own keys)
|
|
612
|
+
this.spinner.updateText(`🔧 Generating TypeScript types with ${await this.ai.getActiveProviderName()}...`);
|
|
613
|
+
const { text, provider } = await this.ai.generateText(prompt, {
|
|
614
|
+
model: options.model || process.env.MYCONTEXT_MODEL,
|
|
615
|
+
modelCandidates: this.getModelCandidates(options),
|
|
616
|
+
});
|
|
617
|
+
// Parse the generated content and create structured files
|
|
618
|
+
const structuredContent = this.parseAndStructureTypes(text);
|
|
619
|
+
return {
|
|
620
|
+
success: true,
|
|
621
|
+
content: structuredContent,
|
|
622
|
+
provider: provider,
|
|
623
|
+
metadata: {
|
|
624
|
+
model: "local",
|
|
625
|
+
tokens: structuredContent.length / 4,
|
|
626
|
+
latency: 600,
|
|
627
|
+
},
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
// No local keys - use hosted API (requires authentication)
|
|
632
|
+
this.spinner.updateText("🔧 Generating TypeScript types with MyContext AI (hosted)...");
|
|
633
|
+
const hostedResult = await this.hostedApi.generateContext("types", prompt, {
|
|
634
|
+
model: options.model || "mycontext",
|
|
635
|
+
context: projectContext,
|
|
636
|
+
});
|
|
637
|
+
if (hostedResult.success && hostedResult.data) {
|
|
638
|
+
// Parse the generated content and create structured files
|
|
639
|
+
const structuredContent = this.parseAndStructureTypes(hostedResult.data);
|
|
640
|
+
return {
|
|
641
|
+
success: true,
|
|
642
|
+
content: structuredContent,
|
|
643
|
+
provider: "hosted",
|
|
644
|
+
metadata: {
|
|
645
|
+
model: "mycontext",
|
|
646
|
+
tokens: structuredContent.length / 4,
|
|
647
|
+
latency: 600,
|
|
648
|
+
},
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
throw new Error(hostedResult.error || "Hosted API failed");
|
|
653
|
+
}
|
|
654
|
+
}
|
|
556
655
|
}
|
|
557
656
|
catch (error) {
|
|
558
657
|
return {
|
|
@@ -1123,23 +1222,53 @@ Create a globals.css file with proper CSS custom properties following this exact
|
|
|
1123
1222
|
|
|
1124
1223
|
Make the CSS immediately usable - no placeholders, actual working values!`;
|
|
1125
1224
|
try {
|
|
1126
|
-
|
|
1127
|
-
const
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1225
|
+
// Check if user has local AI keys configured
|
|
1226
|
+
const hasLocalKeys = this.hasLocalAIKeys();
|
|
1227
|
+
if (hasLocalKeys) {
|
|
1228
|
+
// Use local AI first (user's own keys)
|
|
1229
|
+
this.spinner.updateText(`🎨 Generating brand system with ${await this.ai.getActiveProviderName()}...`);
|
|
1230
|
+
const { text, provider } = await this.ai.generateText(prompt, {
|
|
1231
|
+
model: options.model || process.env.MYCONTEXT_MODEL,
|
|
1232
|
+
modelCandidates: this.getModelCandidates(options),
|
|
1233
|
+
});
|
|
1234
|
+
// Parse and create the brand system files
|
|
1235
|
+
const brandFiles = this.parseAndCreateBrandSystem(text);
|
|
1236
|
+
return {
|
|
1237
|
+
success: true,
|
|
1238
|
+
content: brandFiles.guide,
|
|
1239
|
+
provider: provider,
|
|
1240
|
+
metadata: {
|
|
1241
|
+
model: "local",
|
|
1242
|
+
tokens: brandFiles.guide.length / 4,
|
|
1243
|
+
latency: 600,
|
|
1244
|
+
},
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
else {
|
|
1248
|
+
// No local keys - use hosted API (requires authentication)
|
|
1249
|
+
this.spinner.updateText("🎨 Generating brand system with MyContext AI (hosted)...");
|
|
1250
|
+
const hostedResult = await this.hostedApi.generateContext("brand", prompt, {
|
|
1251
|
+
model: options.model || "mycontext",
|
|
1252
|
+
context: projectContext,
|
|
1253
|
+
});
|
|
1254
|
+
if (hostedResult.success && hostedResult.data) {
|
|
1255
|
+
// Parse and create the brand system files
|
|
1256
|
+
const brandFiles = this.parseAndCreateBrandSystem(hostedResult.data);
|
|
1257
|
+
return {
|
|
1258
|
+
success: true,
|
|
1259
|
+
content: brandFiles.guide,
|
|
1260
|
+
provider: "hosted",
|
|
1261
|
+
metadata: {
|
|
1262
|
+
model: "mycontext",
|
|
1263
|
+
tokens: brandFiles.guide.length / 4,
|
|
1264
|
+
latency: 600,
|
|
1265
|
+
},
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
else {
|
|
1269
|
+
throw new Error(hostedResult.error || "Hosted API failed");
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1143
1272
|
}
|
|
1144
1273
|
catch (error) {
|
|
1145
1274
|
return {
|
|
@@ -1279,31 +1408,51 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
|
|
|
1279
1408
|
"- Provide up to 3 'coreCandidates' that best represent the design anchor (canvas/layout), with a short reason.",
|
|
1280
1409
|
].join("\n");
|
|
1281
1410
|
try {
|
|
1282
|
-
|
|
1411
|
+
// Check if user has local AI keys configured
|
|
1412
|
+
const hasLocalKeys = this.hasLocalAIKeys();
|
|
1283
1413
|
let text;
|
|
1284
1414
|
let provider;
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
text = r.text;
|
|
1291
|
-
provider = r.provider;
|
|
1292
|
-
}
|
|
1293
|
-
catch (e) {
|
|
1294
|
-
// Handle transient network issues like unexpected EOF with a single retry
|
|
1295
|
-
const msg = String(e?.message || e);
|
|
1296
|
-
if (/unexpected EOF|ECONNRESET|EPIPE/i.test(msg)) {
|
|
1297
|
-
this.spinner.updateText("Retrying component list generation after transient error...");
|
|
1298
|
-
const r2 = await this.ai.generateText(prompt, {
|
|
1415
|
+
if (hasLocalKeys) {
|
|
1416
|
+
// Use local AI first (user's own keys)
|
|
1417
|
+
this.spinner.updateText(`📋 Generating component list with ${await this.ai.getActiveProviderName()}...`);
|
|
1418
|
+
try {
|
|
1419
|
+
const r = await this.ai.generateText(prompt, {
|
|
1299
1420
|
model: options.model || process.env.MYCONTEXT_MODEL,
|
|
1300
1421
|
modelCandidates: this.getModelCandidates(options),
|
|
1301
1422
|
});
|
|
1302
|
-
text =
|
|
1303
|
-
provider =
|
|
1423
|
+
text = r.text;
|
|
1424
|
+
provider = r.provider;
|
|
1425
|
+
}
|
|
1426
|
+
catch (e) {
|
|
1427
|
+
// Handle transient network issues like unexpected EOF with a single retry
|
|
1428
|
+
const msg = String(e?.message || e);
|
|
1429
|
+
if (/unexpected EOF|ECONNRESET|EPIPE/i.test(msg)) {
|
|
1430
|
+
this.spinner.updateText("Retrying component list generation after transient error...");
|
|
1431
|
+
const r2 = await this.ai.generateText(prompt, {
|
|
1432
|
+
model: options.model || process.env.MYCONTEXT_MODEL,
|
|
1433
|
+
modelCandidates: this.getModelCandidates(options),
|
|
1434
|
+
});
|
|
1435
|
+
text = r2.text;
|
|
1436
|
+
provider = r2.provider;
|
|
1437
|
+
}
|
|
1438
|
+
else {
|
|
1439
|
+
throw e;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
else {
|
|
1444
|
+
// No local keys - use hosted API (requires authentication)
|
|
1445
|
+
this.spinner.updateText("📋 Generating component list with MyContext AI (hosted)...");
|
|
1446
|
+
const hostedResult = await this.hostedApi.generateContext("components-list", prompt, {
|
|
1447
|
+
model: options.model || "mycontext",
|
|
1448
|
+
context: projectContext,
|
|
1449
|
+
});
|
|
1450
|
+
if (hostedResult.success && hostedResult.data) {
|
|
1451
|
+
text = hostedResult.data;
|
|
1452
|
+
provider = "hosted";
|
|
1304
1453
|
}
|
|
1305
1454
|
else {
|
|
1306
|
-
throw
|
|
1455
|
+
throw new Error(hostedResult.error || "Hosted API failed");
|
|
1307
1456
|
}
|
|
1308
1457
|
}
|
|
1309
1458
|
// Attempt to repair and extract valid JSON
|
|
@@ -2774,6 +2923,14 @@ ${isEcommerce
|
|
|
2774
2923
|
}
|
|
2775
2924
|
catch { }
|
|
2776
2925
|
}
|
|
2926
|
+
hasLocalAIKeys() {
|
|
2927
|
+
// Check for any local AI provider keys
|
|
2928
|
+
return !!(process.env.MYCONTEXT_GITHUB_TOKEN ||
|
|
2929
|
+
process.env.MYCONTEXT_QWEN_API_KEY ||
|
|
2930
|
+
process.env.OPENAI_API_KEY ||
|
|
2931
|
+
process.env.ANTHROPIC_API_KEY ||
|
|
2932
|
+
process.env.HUGGINGFACE_API_KEY);
|
|
2933
|
+
}
|
|
2777
2934
|
getModelCandidates(options) {
|
|
2778
2935
|
const raw = options.modelCandidates ||
|
|
2779
2936
|
options.models ||
|