mycontext-cli 2.0.28 → 2.0.30

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 (69) hide show
  1. package/README.md +106 -14
  2. package/dist/cli.js +89 -99
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/generate-components.d.ts +10 -0
  5. package/dist/commands/generate-components.d.ts.map +1 -1
  6. package/dist/commands/generate-components.js +300 -3
  7. package/dist/commands/generate-components.js.map +1 -1
  8. package/dist/commands/generate-context-files.d.ts +9 -0
  9. package/dist/commands/generate-context-files.d.ts.map +1 -1
  10. package/dist/commands/generate-context-files.js +57 -0
  11. package/dist/commands/generate-context-files.js.map +1 -1
  12. package/dist/commands/generate.d.ts +5 -0
  13. package/dist/commands/generate.d.ts.map +1 -1
  14. package/dist/commands/generate.js +75 -1
  15. package/dist/commands/generate.js.map +1 -1
  16. package/dist/commands/init.d.ts +9 -0
  17. package/dist/commands/init.d.ts.map +1 -1
  18. package/dist/commands/init.js +223 -68
  19. package/dist/commands/init.js.map +1 -1
  20. package/dist/commands/preview-components.d.ts +12 -0
  21. package/dist/commands/preview-components.d.ts.map +1 -0
  22. package/dist/commands/preview-components.js +122 -0
  23. package/dist/commands/preview-components.js.map +1 -0
  24. package/dist/commands/refine-component.d.ts +43 -0
  25. package/dist/commands/refine-component.d.ts.map +1 -0
  26. package/dist/commands/refine-component.js +313 -0
  27. package/dist/commands/refine-component.js.map +1 -0
  28. package/dist/commands/review-context.d.ts +47 -0
  29. package/dist/commands/review-context.d.ts.map +1 -0
  30. package/dist/commands/review-context.js +335 -0
  31. package/dist/commands/review-context.js.map +1 -0
  32. package/dist/package.json +11 -2
  33. package/dist/services/ContextValidator.d.ts +99 -0
  34. package/dist/services/ContextValidator.d.ts.map +1 -0
  35. package/dist/services/ContextValidator.js +433 -0
  36. package/dist/services/ContextValidator.js.map +1 -0
  37. package/dist/services/MutationLogger.d.ts +54 -0
  38. package/dist/services/MutationLogger.d.ts.map +1 -0
  39. package/dist/services/MutationLogger.js +164 -0
  40. package/dist/services/MutationLogger.js.map +1 -0
  41. package/dist/services/RegressionRunner.d.ts +49 -0
  42. package/dist/services/RegressionRunner.d.ts.map +1 -0
  43. package/dist/services/RegressionRunner.js +285 -0
  44. package/dist/services/RegressionRunner.js.map +1 -0
  45. package/dist/services/TriggerLogger.d.ts +101 -0
  46. package/dist/services/TriggerLogger.d.ts.map +1 -0
  47. package/dist/services/TriggerLogger.js +263 -0
  48. package/dist/services/TriggerLogger.js.map +1 -0
  49. package/dist/templates/instantdb/db.template.ts +14 -0
  50. package/dist/templates/instantdb/home-client.template.tsx +127 -0
  51. package/dist/templates/instantdb/page.template.tsx +5 -0
  52. package/dist/templates/instantdb/perms.template.ts +9 -0
  53. package/dist/templates/instantdb/schema.template.ts +28 -0
  54. package/dist/templates/playbooks/instantdb-integration.md +851 -0
  55. package/dist/templates/playbooks/mpesa-integration.md +652 -0
  56. package/dist/templates/pm-integration-config.json +20 -0
  57. package/dist/templates/ui-spec-examples.md +318 -0
  58. package/dist/templates/ui-spec-templates.json +244 -0
  59. package/dist/utils/envExampleGenerator.d.ts.map +1 -1
  60. package/dist/utils/envExampleGenerator.js +9 -3
  61. package/dist/utils/envExampleGenerator.js.map +1 -1
  62. package/dist/utils/hybridAIClient.d.ts.map +1 -1
  63. package/dist/utils/hybridAIClient.js +21 -0
  64. package/dist/utils/hybridAIClient.js.map +1 -1
  65. package/dist/utils/openRouterClient.d.ts +10 -0
  66. package/dist/utils/openRouterClient.d.ts.map +1 -0
  67. package/dist/utils/openRouterClient.js +61 -0
  68. package/dist/utils/openRouterClient.js.map +1 -0
  69. package/package.json +11 -2
@@ -46,6 +46,8 @@ const chalk_1 = __importDefault(require("chalk"));
46
46
  const fs = __importStar(require("fs-extra"));
47
47
  const path = __importStar(require("path"));
48
48
  const child_process_1 = require("child_process");
49
+ const review_context_1 = require("./review-context");
50
+ const TriggerLogger_1 = require("../services/TriggerLogger");
49
51
  // --- Orchestration in GenerateComponentsCommand ---
50
52
  class GenerateComponentsCommand {
51
53
  constructor() {
@@ -53,6 +55,7 @@ class GenerateComponentsCommand {
53
55
  this.hostedApi = new hostedApiClient_1.HostedApiClient();
54
56
  this.architectureEngine = new completeArchitectureEngine_1.CompleteArchitectureEngine();
55
57
  this.contextLoader = new unifiedDesignContextLoader_1.UnifiedDesignContextLoader();
58
+ this.triggerLogger = new TriggerLogger_1.TriggerLogger();
56
59
  this.contextArtifacts = {
57
60
  prd: "",
58
61
  types: "",
@@ -288,15 +291,21 @@ class GenerateComponentsCommand {
288
291
  }
289
292
  // Determine if we're generating a specific group or all components
290
293
  const isAll = target === "all" || options.all || options["--all"];
291
- const groupName = isAll ? undefined : target;
292
- if (isAll) {
294
+ const isCoreOnly = target === "core" ||
295
+ options.coreOnly ||
296
+ options["--core-only"];
297
+ const groupName = isAll || isCoreOnly ? undefined : target;
298
+ if (isCoreOnly) {
299
+ await this.generateCoreComponents(options, spinner, userInfo.userId);
300
+ }
301
+ else if (isAll) {
293
302
  await this.generateAllComponents(options, spinner, userInfo.userId);
294
303
  }
295
304
  else if (groupName) {
296
305
  await this.generateComponentGroup(groupName, options, spinner, userInfo.userId);
297
306
  }
298
307
  else {
299
- throw new Error("Please specify a group name or 'all' to generate components");
308
+ throw new Error("Please specify a group name, 'all', or use --core-only to generate components");
300
309
  }
301
310
  // Optionally run final canvas normalization
302
311
  if (options.finalCanvas) {
@@ -550,6 +559,294 @@ class GenerateComponentsCommand {
550
559
  catch { }
551
560
  }
552
561
  }
562
+ async generateCoreComponents(options, spinner, userId) {
563
+ spinner.updateText("Generating core 10 components for validation...");
564
+ // Check if critical gaps are addressed
565
+ const criticalGapsAddressed = await review_context_1.ReviewContextCommand.areCriticalGapsAddressed();
566
+ if (!criticalGapsAddressed) {
567
+ console.log(chalk_1.default.red("❌ Critical gaps must be addressed before generating components"));
568
+ console.log(chalk_1.default.blue("💡 Run 'mycontext review:context' to address critical gaps"));
569
+ return;
570
+ }
571
+ // Read component list
572
+ let componentListPath = ".mycontext/04-component-list.json";
573
+ if (!(await this.fs.exists(componentListPath))) {
574
+ componentListPath = ".mycontext/component-list.json";
575
+ }
576
+ if (!(await this.fs.exists(componentListPath))) {
577
+ componentListPath = "context/component-list.json";
578
+ }
579
+ if (!(await this.fs.exists(componentListPath))) {
580
+ throw new Error("Component list not found. Run 'mycontext generate components-list' first.");
581
+ }
582
+ const componentList = JSON.parse(await this.fs.readFile(componentListPath));
583
+ const groups = this.convertHierarchicalToFlat(componentList);
584
+ if (groups.length === 0) {
585
+ throw new Error("No component groups found in component-list.json");
586
+ }
587
+ // Select first 10 components across all groups
588
+ const coreComponents = [];
589
+ let componentCount = 0;
590
+ const maxCoreComponents = 10;
591
+ for (const group of groups) {
592
+ if (componentCount >= maxCoreComponents)
593
+ break;
594
+ const components = group.components || [];
595
+ for (const component of components) {
596
+ if (componentCount >= maxCoreComponents)
597
+ break;
598
+ coreComponents.push({
599
+ ...component,
600
+ groupName: group.name,
601
+ group: group,
602
+ });
603
+ componentCount++;
604
+ }
605
+ }
606
+ if (coreComponents.length === 0) {
607
+ throw new Error("No components found in component-list.json");
608
+ }
609
+ console.log(chalk_1.default.blue(`🎯 Generating ${coreComponents.length} core components for validation`));
610
+ // Ensure shadcn/ui components are available
611
+ await this.ensureShadcnComponentsInstalled(groups, spinner);
612
+ // Ensure form dependencies exist
613
+ await this.ensureFormDeps(spinner);
614
+ // Ensure test scaffold exists (optional)
615
+ if (options.withTests) {
616
+ await this.ensureTestsScaffold(spinner);
617
+ }
618
+ // Create core components directory structure
619
+ const componentsDir = options.output || path.join(".mycontext", "components");
620
+ const mobileDir = path.join(componentsDir, "mobile");
621
+ const desktopDir = path.join(componentsDir, "desktop");
622
+ await this.fs.ensureDir(mobileDir);
623
+ await this.fs.ensureDir(desktopDir);
624
+ let generatedCount = 0;
625
+ // Generate mobile and desktop variants for each core component
626
+ for (const component of coreComponents) {
627
+ spinner.updateText(`Generating ${component.name} (${generatedCount + 1}/${coreComponents.length})...`);
628
+ // Check if component feature is approved
629
+ const featureId = component.name.toLowerCase().replace(/\s+/g, "-");
630
+ const approval = await review_context_1.ReviewContextCommand.getFeatureApproval(featureId);
631
+ if (approval === false) {
632
+ console.log(chalk_1.default.yellow(` ⏭️ Skipping ${component.name} (rejected in review)`));
633
+ continue;
634
+ }
635
+ if (approval === null) {
636
+ console.log(chalk_1.default.yellow(` ⚠️ ${component.name} not reviewed yet - generating anyway`));
637
+ }
638
+ // Generate mobile variant
639
+ await this.generateComponentVariant(component, "mobile", mobileDir, options, userId);
640
+ // Generate desktop variant
641
+ await this.generateComponentVariant(component, "desktop", desktopDir, options, userId);
642
+ // Generate tests if requested
643
+ if (options.withTests) {
644
+ await this.generateComponentTest(component, mobileDir);
645
+ await this.generateComponentTest(component, desktopDir);
646
+ }
647
+ generatedCount++;
648
+ }
649
+ // Generate index files for both variants
650
+ await this.generateCoreIndexFiles(coreComponents, mobileDir, desktopDir);
651
+ // Update preview registry
652
+ if (options.updatePreview !== false) {
653
+ await this.updatePreviewRegistry(componentsDir);
654
+ await this.ensurePreviewRoute();
655
+ }
656
+ // Post-generation checks
657
+ if (options.check) {
658
+ await this.runPostGenerationChecks(componentsDir);
659
+ }
660
+ // Open preview if requested
661
+ if (options.openPreview !== false) {
662
+ await this.openPreview();
663
+ }
664
+ console.log(chalk_1.default.green(`✅ Generated ${generatedCount} core components with mobile and desktop variants`));
665
+ console.log(chalk_1.default.blue(`📁 Components saved to: ${componentsDir}/mobile/ and ${componentsDir}/desktop/`));
666
+ console.log(chalk_1.default.yellow(`🔍 Next: Run 'mycontext preview:components' to validate the core components`));
667
+ // Log trigger event
668
+ await this.triggerLogger.logTrigger("component-refinement", `Generated ${generatedCount} core components for validation`, coreComponents.map((c) => c.name), "Core component generation completed");
669
+ }
670
+ async generateComponentVariant(component, variant, outputDir, options, userId) {
671
+ try {
672
+ // Check if user has local AI keys configured
673
+ const hasLocalKeys = this.hasLocalAIKeys();
674
+ let codeResult;
675
+ if (hasLocalKeys) {
676
+ // Use local AI first (user's own keys) - sub-agent orchestration
677
+ const { orchestrator } = await Promise.resolve().then(() => __importStar(require("../agents/orchestrator/SubAgentOrchestrator")));
678
+ // Execute code generation with retry logic
679
+ let retryCount = 0;
680
+ const maxRetries = 3;
681
+ const baseDelay = 2000; // 2 seconds base delay
682
+ while (retryCount <= maxRetries) {
683
+ try {
684
+ console.log(`🔍 DEBUG: About to call orchestrator.executeAgent for CodeGenSubAgent (attempt ${retryCount + 1}/${maxRetries + 1})`);
685
+ // Use stack configuration timeout if available
686
+ const timeout = this.stackConfig?.timeouts?.generation || 60000;
687
+ // Get enriched context for better AI generation
688
+ const { enrichedContext } = await this.contextLoader.loadUnifiedDesignContext();
689
+ const formattedContext = this.contextLoader
690
+ .getContextEnricher()
691
+ .formatContextForModel(enrichedContext);
692
+ const result = await orchestrator.executeAgent("CodeGenSubAgent", {
693
+ componentName: component.name,
694
+ componentDescription: component.description,
695
+ componentType: component.type,
696
+ variant: variant,
697
+ groupName: component.groupName,
698
+ dependencies: component.dependencies || [],
699
+ enrichedContext: formattedContext,
700
+ userId: userId,
701
+ timeout: timeout,
702
+ temperature: options.temperature || 0.7,
703
+ maxTokens: options.maxTokens || 4000,
704
+ });
705
+ if (result && result.code) {
706
+ codeResult = result;
707
+ break; // Success, exit retry loop
708
+ }
709
+ else {
710
+ throw new Error("No code generated");
711
+ }
712
+ }
713
+ catch (error) {
714
+ console.log(chalk_1.default.yellow(`⚠️ Generation attempt ${retryCount + 1} failed: ${error instanceof Error ? error.message : "Unknown error"}`));
715
+ retryCount++;
716
+ if (retryCount <= maxRetries) {
717
+ const delay = baseDelay * Math.pow(2, retryCount - 1); // Exponential backoff
718
+ console.log(chalk_1.default.gray(` Retrying in ${delay / 1000} seconds...`));
719
+ await new Promise((resolve) => setTimeout(resolve, delay));
720
+ }
721
+ }
722
+ }
723
+ if (!codeResult) {
724
+ throw new Error("All generation attempts failed");
725
+ }
726
+ }
727
+ else {
728
+ // No local keys - try hosted API only
729
+ console.log(chalk_1.default.blue("🔧 Using hosted API for component generation..."));
730
+ try {
731
+ const hostedResult = await this.hostedApi.generateComponent(component.description, {
732
+ componentName: component.name,
733
+ model: "mycontext",
734
+ context: {
735
+ prd: this.contextArtifacts.prd,
736
+ types: this.contextArtifacts.types,
737
+ brand: this.contextArtifacts.brand,
738
+ componentList: this.contextArtifacts.compList,
739
+ },
740
+ variant: variant,
741
+ });
742
+ if (hostedResult.success && hostedResult.data) {
743
+ codeResult = {
744
+ code: hostedResult.data,
745
+ metadata: {
746
+ model: "hosted",
747
+ tokens: hostedResult.data.length / 4,
748
+ latency: 600,
749
+ },
750
+ };
751
+ }
752
+ else {
753
+ throw new Error("Hosted API generation failed");
754
+ }
755
+ }
756
+ catch (error) {
757
+ console.log(chalk_1.default.red("❌ Hosted API failed"));
758
+ console.log(chalk_1.default.yellow("💡 MyContext requires 100% accuracy - no fallbacks"));
759
+ console.log(chalk_1.default.blue("🔄 Retry options:"));
760
+ console.log(chalk_1.default.gray(" 1. Configure a local AI provider API key"));
761
+ console.log(chalk_1.default.gray(" 2. Check your API key configuration"));
762
+ console.log(chalk_1.default.gray(" 3. Try again later with: mycontext generate components"));
763
+ throw new Error("Hosted API unavailable - configure local AI provider");
764
+ }
765
+ }
766
+ // Write component file
767
+ const fileName = `${component.name}.tsx`;
768
+ const filePath = path.join(outputDir, fileName);
769
+ // Add variant-specific styling and imports
770
+ const variantCode = this.addVariantSpecificCode(codeResult.code, variant);
771
+ await this.fs.writeFile(filePath, variantCode);
772
+ console.log(chalk_1.default.green(` ✅ Generated ${variant} variant: ${fileName}`));
773
+ }
774
+ catch (error) {
775
+ console.log(chalk_1.default.red(` ❌ Failed to generate ${variant} variant for ${component.name}: ${error instanceof Error ? error.message : "Unknown error"}`));
776
+ throw error;
777
+ }
778
+ }
779
+ addVariantSpecificCode(code, variant) {
780
+ // Add variant-specific styling and responsive behavior
781
+ const variantImports = variant === "mobile"
782
+ ? `import { cn } from "@/lib/utils";\n`
783
+ : `import { cn } from "@/lib/utils";\n`;
784
+ const variantStyles = variant === "mobile"
785
+ ? `className={cn("min-h-[44px] min-w-[44px] p-2 text-sm", className)}`
786
+ : `className={cn("min-h-[32px] min-w-[32px] p-4 text-base", className)}`;
787
+ // Replace className patterns with variant-specific ones
788
+ let modifiedCode = code.replace(/className=\{cn\([^}]+\)\}/g, variantStyles);
789
+ // Add variant-specific imports if not present
790
+ if (!modifiedCode.includes("import { cn }")) {
791
+ modifiedCode = variantImports + modifiedCode;
792
+ }
793
+ return modifiedCode;
794
+ }
795
+ async generateCoreIndexFiles(coreComponents, mobileDir, desktopDir) {
796
+ // Generate mobile index
797
+ const mobileIndexContent = coreComponents
798
+ .map((comp) => `export { ${comp.name} } from './${comp.name}';`)
799
+ .join("\n");
800
+ await this.fs.writeFile(path.join(mobileDir, "index.ts"), mobileIndexContent);
801
+ // Generate desktop index
802
+ const desktopIndexContent = coreComponents
803
+ .map((comp) => `export { ${comp.name} } from './${comp.name}';`)
804
+ .join("\n");
805
+ await this.fs.writeFile(path.join(desktopDir, "index.ts"), desktopIndexContent);
806
+ console.log(chalk_1.default.green(" ✅ Generated index files for mobile and desktop variants"));
807
+ }
808
+ async openPreview() {
809
+ try {
810
+ // Try to open in browser
811
+ const url = "http://localhost:3000/mycontext-preview";
812
+ // Check if Next.js dev server is running
813
+ try {
814
+ (0, child_process_1.execSync)("curl -s http://localhost:3000 > /dev/null", {
815
+ stdio: "pipe",
816
+ });
817
+ console.log(chalk_1.default.green(`✅ Opening preview at ${url}`));
818
+ // Open in browser
819
+ (0, child_process_1.execSync)(`open ${url}`, { stdio: "pipe" });
820
+ }
821
+ catch {
822
+ console.log(chalk_1.default.yellow("⚠️ Next.js dev server not running"));
823
+ console.log(chalk_1.default.blue("💡 Start your Next.js app with: npm run dev"));
824
+ console.log(chalk_1.default.blue(` Then visit: ${url}`));
825
+ }
826
+ }
827
+ catch (error) {
828
+ console.log(chalk_1.default.yellow("⚠️ Could not open browser automatically"));
829
+ console.log(chalk_1.default.blue("💡 Manually visit: http://localhost:3000/mycontext-preview"));
830
+ }
831
+ }
832
+ async runPostGenerationChecks(componentsDir) {
833
+ console.log(chalk_1.default.blue("🔍 Running post-generation checks..."));
834
+ try {
835
+ // TypeScript check
836
+ console.log(chalk_1.default.gray(" Running TypeScript check..."));
837
+ (0, child_process_1.execSync)("npx tsc --noEmit", { stdio: "pipe" });
838
+ console.log(chalk_1.default.green(" ✅ TypeScript check passed"));
839
+ // Lint check
840
+ console.log(chalk_1.default.gray(" Running ESLint..."));
841
+ (0, child_process_1.execSync)("npx eslint .mycontext/components --ext .ts,.tsx", {
842
+ stdio: "pipe",
843
+ });
844
+ console.log(chalk_1.default.green(" ✅ ESLint check passed"));
845
+ }
846
+ catch (error) {
847
+ console.log(chalk_1.default.yellow(" ⚠️ Some checks failed, but components were generated"));
848
+ }
849
+ }
553
850
  async generateComponentGroup(groupName, options, spinner, userId) {
554
851
  spinner.updateText(`Generating ${groupName} components...`);
555
852
  // Read component list