mycontext-cli 4.2.16 → 4.2.18

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 (167) hide show
  1. package/README.md +62 -96
  2. package/dist/agents/implementations/SolverAgent.d.ts +42 -0
  3. package/dist/agents/implementations/SolverAgent.d.ts.map +1 -0
  4. package/dist/agents/implementations/SolverAgent.js +543 -0
  5. package/dist/agents/implementations/SolverAgent.js.map +1 -0
  6. package/dist/cli.js +143 -43
  7. package/dist/cli.js.map +1 -1
  8. package/dist/clients/ClaudeSDKClient.d.ts +1 -0
  9. package/dist/clients/ClaudeSDKClient.d.ts.map +1 -1
  10. package/dist/clients/ClaudeSDKClient.js +3 -0
  11. package/dist/clients/ClaudeSDKClient.js.map +1 -1
  12. package/dist/clients/MyContextAIClient.d.ts +4 -0
  13. package/dist/clients/MyContextAIClient.d.ts.map +1 -1
  14. package/dist/clients/MyContextAIClient.js +6 -0
  15. package/dist/clients/MyContextAIClient.js.map +1 -1
  16. package/dist/clients/ProviderChain.d.ts +1 -0
  17. package/dist/clients/ProviderChain.d.ts.map +1 -1
  18. package/dist/clients/ProviderChain.js +3 -0
  19. package/dist/clients/ProviderChain.js.map +1 -1
  20. package/dist/clients/XAIClient.d.ts +4 -0
  21. package/dist/clients/XAIClient.d.ts.map +1 -1
  22. package/dist/clients/XAIClient.js +6 -0
  23. package/dist/clients/XAIClient.js.map +1 -1
  24. package/dist/commands/add.d.ts +17 -0
  25. package/dist/commands/add.d.ts.map +1 -0
  26. package/dist/commands/add.js +164 -0
  27. package/dist/commands/add.js.map +1 -0
  28. package/dist/commands/build.d.ts +10 -0
  29. package/dist/commands/build.d.ts.map +1 -0
  30. package/dist/commands/build.js +37 -0
  31. package/dist/commands/build.js.map +1 -0
  32. package/dist/commands/generate-assets.d.ts +11 -0
  33. package/dist/commands/generate-assets.d.ts.map +1 -0
  34. package/dist/commands/generate-assets.js +131 -0
  35. package/dist/commands/generate-assets.js.map +1 -0
  36. package/dist/commands/generate-components.d.ts.map +1 -1
  37. package/dist/commands/generate-components.js +21 -18
  38. package/dist/commands/generate-components.js.map +1 -1
  39. package/dist/commands/generate-screens.d.ts +5 -0
  40. package/dist/commands/generate-screens.d.ts.map +1 -1
  41. package/dist/commands/generate-screens.js +160 -12
  42. package/dist/commands/generate-screens.js.map +1 -1
  43. package/dist/commands/generate.d.ts +3 -0
  44. package/dist/commands/generate.d.ts.map +1 -1
  45. package/dist/commands/generate.js +285 -167
  46. package/dist/commands/generate.js.map +1 -1
  47. package/dist/commands/ideate.d.ts +14 -0
  48. package/dist/commands/ideate.d.ts.map +1 -0
  49. package/dist/commands/ideate.js +153 -0
  50. package/dist/commands/ideate.js.map +1 -0
  51. package/dist/commands/init-interactive.d.ts.map +1 -1
  52. package/dist/commands/init-interactive.js +30 -2
  53. package/dist/commands/init-interactive.js.map +1 -1
  54. package/dist/commands/init.d.ts +2 -0
  55. package/dist/commands/init.d.ts.map +1 -1
  56. package/dist/commands/init.js +220 -39
  57. package/dist/commands/init.js.map +1 -1
  58. package/dist/commands/migrate-transform.d.ts +16 -0
  59. package/dist/commands/migrate-transform.d.ts.map +1 -0
  60. package/dist/commands/migrate-transform.js +135 -0
  61. package/dist/commands/migrate-transform.js.map +1 -0
  62. package/dist/commands/setup-complete.d.ts.map +1 -1
  63. package/dist/commands/setup-complete.js +4 -6
  64. package/dist/commands/setup-complete.js.map +1 -1
  65. package/dist/commands/sync-readme.d.ts +3 -1
  66. package/dist/commands/sync-readme.d.ts.map +1 -1
  67. package/dist/commands/sync-readme.js +57 -59
  68. package/dist/commands/sync-readme.js.map +1 -1
  69. package/dist/commands/vision-test.d.ts.map +1 -1
  70. package/dist/commands/vision-test.js +8 -0
  71. package/dist/commands/vision-test.js.map +1 -1
  72. package/dist/commands/workflow.d.ts.map +1 -1
  73. package/dist/commands/workflow.js +10 -58
  74. package/dist/commands/workflow.js.map +1 -1
  75. package/dist/constants/subAgentPersonalities.d.ts.map +1 -1
  76. package/dist/constants/subAgentPersonalities.js +29 -0
  77. package/dist/constants/subAgentPersonalities.js.map +1 -1
  78. package/dist/core/ai/AICore.d.ts +12 -0
  79. package/dist/core/ai/AICore.d.ts.map +1 -1
  80. package/dist/core/ai/AICore.js +56 -1
  81. package/dist/core/ai/AICore.js.map +1 -1
  82. package/dist/core/ai/TokenCostModel.d.ts +37 -0
  83. package/dist/core/ai/TokenCostModel.d.ts.map +1 -0
  84. package/dist/core/ai/TokenCostModel.js +132 -0
  85. package/dist/core/ai/TokenCostModel.js.map +1 -0
  86. package/dist/core/brain/BrainClient.d.ts +13 -1
  87. package/dist/core/brain/BrainClient.d.ts.map +1 -1
  88. package/dist/core/brain/BrainClient.js +49 -0
  89. package/dist/core/brain/BrainClient.js.map +1 -1
  90. package/dist/interfaces/AIClient.d.ts +4 -0
  91. package/dist/interfaces/AIClient.d.ts.map +1 -1
  92. package/dist/interfaces/AIClient.js.map +1 -1
  93. package/dist/services/MonorepoScanner.d.ts +18 -0
  94. package/dist/services/MonorepoScanner.d.ts.map +1 -0
  95. package/dist/services/MonorepoScanner.js +101 -0
  96. package/dist/services/MonorepoScanner.js.map +1 -0
  97. package/dist/services/ProjectScanner.d.ts.map +1 -1
  98. package/dist/services/ProjectScanner.js +7 -0
  99. package/dist/services/ProjectScanner.js.map +1 -1
  100. package/dist/services/ReadmeDeducer.d.ts +14 -0
  101. package/dist/services/ReadmeDeducer.d.ts.map +1 -0
  102. package/dist/services/ReadmeDeducer.js +109 -0
  103. package/dist/services/ReadmeDeducer.js.map +1 -0
  104. package/dist/tui/DashboardMode.d.ts +16 -0
  105. package/dist/tui/DashboardMode.d.ts.map +1 -0
  106. package/dist/tui/DashboardMode.js +190 -0
  107. package/dist/tui/DashboardMode.js.map +1 -0
  108. package/dist/tui/TUIClient.d.ts +9 -0
  109. package/dist/tui/TUIClient.d.ts.map +1 -1
  110. package/dist/tui/TUIClient.js +63 -0
  111. package/dist/tui/TUIClient.js.map +1 -1
  112. package/dist/types/constraint.d.ts +123 -0
  113. package/dist/types/constraint.d.ts.map +1 -0
  114. package/dist/types/constraint.js +13 -0
  115. package/dist/types/constraint.js.map +1 -0
  116. package/dist/types/index.d.ts +1 -1
  117. package/dist/types/index.d.ts.map +1 -1
  118. package/dist/types/living-context.d.ts +7 -0
  119. package/dist/types/living-context.d.ts.map +1 -1
  120. package/dist/types/tui.d.ts +3 -1
  121. package/dist/types/tui.d.ts.map +1 -1
  122. package/dist/utils/claudeAgentClient.d.ts +4 -0
  123. package/dist/utils/claudeAgentClient.d.ts.map +1 -1
  124. package/dist/utils/claudeAgentClient.js +6 -0
  125. package/dist/utils/claudeAgentClient.js.map +1 -1
  126. package/dist/utils/contextEnricher.d.ts +2 -1
  127. package/dist/utils/contextEnricher.d.ts.map +1 -1
  128. package/dist/utils/contextEnricher.js +138 -1
  129. package/dist/utils/contextEnricher.js.map +1 -1
  130. package/dist/utils/contextRenderer.d.ts +3 -0
  131. package/dist/utils/contextRenderer.d.ts.map +1 -1
  132. package/dist/utils/contextRenderer.js +101 -26
  133. package/dist/utils/contextRenderer.js.map +1 -1
  134. package/dist/utils/fileSystem.d.ts.map +1 -1
  135. package/dist/utils/fileSystem.js +32 -1
  136. package/dist/utils/fileSystem.js.map +1 -1
  137. package/dist/utils/geminiClient.d.ts +11 -1
  138. package/dist/utils/geminiClient.d.ts.map +1 -1
  139. package/dist/utils/geminiClient.js +109 -56
  140. package/dist/utils/geminiClient.js.map +1 -1
  141. package/dist/utils/githubModelsClient.d.ts +4 -0
  142. package/dist/utils/githubModelsClient.d.ts.map +1 -1
  143. package/dist/utils/githubModelsClient.js +10 -1
  144. package/dist/utils/githubModelsClient.js.map +1 -1
  145. package/dist/utils/openRouterClient.d.ts +1 -0
  146. package/dist/utils/openRouterClient.d.ts.map +1 -1
  147. package/dist/utils/openRouterClient.js +4 -0
  148. package/dist/utils/openRouterClient.js.map +1 -1
  149. package/dist/utils/unifiedDesignContextLoader.d.ts.map +1 -1
  150. package/dist/utils/unifiedDesignContextLoader.js +14 -0
  151. package/dist/utils/unifiedDesignContextLoader.js.map +1 -1
  152. package/package.json +27 -22
  153. package/dist/commands/assemble-features.d.ts +0 -40
  154. package/dist/commands/assemble-features.d.ts.map +0 -1
  155. package/dist/commands/assemble-features.js +0 -383
  156. package/dist/commands/assemble-features.js.map +0 -1
  157. package/dist/commands/compile-prd.d.ts +0 -18
  158. package/dist/commands/compile-prd.d.ts.map +0 -1
  159. package/dist/commands/compile-prd.js +0 -253
  160. package/dist/commands/compile-prd.js.map +0 -1
  161. package/dist/commands/generate-context-files.d.ts +0 -44
  162. package/dist/commands/generate-context-files.d.ts.map +0 -1
  163. package/dist/commands/generate-context-files.js +0 -871
  164. package/dist/commands/generate-context-files.js.map +0 -1
  165. package/dist/templates/playbooks/instantdb-integration.md +0 -851
  166. package/dist/templates/playbooks/mpesa-integration.md +0 -652
  167. package/dist/templates/ui-spec-examples.md +0 -318
@@ -50,6 +50,7 @@ const fs = __importStar(require("fs-extra"));
50
50
  const typeTemplateGenerator_1 = require("../utils/typeTemplateGenerator");
51
51
  const AICore_1 = require("../core/ai/AICore");
52
52
  const contextRenderer_1 = require("../utils/contextRenderer");
53
+ const ProjectScanner_1 = require("../services/ProjectScanner");
53
54
  class GenerateCommand {
54
55
  constructor() {
55
56
  this.fs = new fileSystem_1.FileSystemManager();
@@ -90,29 +91,16 @@ class GenerateCommand {
90
91
  case "context":
91
92
  case "prd":
92
93
  case "requirements":
94
+ // Check if user wants full context generation (PRD + A/B/C/D files)
93
95
  // Check if user wants full context generation (PRD + A/B/C/D files)
94
96
  if (options.full) {
95
97
  // Generate both PRD and A/B/C/D files
96
98
  result = await this.generateFullContext(projectContext, options);
97
99
  }
98
100
  else {
99
- // Default: Generate A/B/C/D files (requires existing PRD)
100
- // This is the expected behavior for 'mycontext generate context'
101
- // Use the dedicated GenerateContextFilesCommand for proper handling
102
- const { GenerateContextFilesCommand } = await Promise.resolve().then(() => __importStar(require("./generate-context-files")));
103
- const contextFilesCommand = new GenerateContextFilesCommand();
104
- await contextFilesCommand.execute({
105
- description: projectContext.description,
106
- projectPath: this.getProjectRoot(),
107
- verbose: options.verbose,
108
- force: options.force,
109
- });
110
- result = {
111
- success: true,
112
- content: "Context files generated successfully",
113
- provider: "hybrid",
114
- metadata: { model: "hybrid", tokens: 0, latency: 0 },
115
- };
101
+ // Default: Direct to full generation as the new standard
102
+ console.log(chalk_1.default.blue("ℹ️ Note: 'generate context' now defaults to full brain synchronization."));
103
+ result = await this.generateFullContext(projectContext, options);
116
104
  }
117
105
  break;
118
106
  case "types":
@@ -202,7 +190,7 @@ class GenerateCommand {
202
190
  console.log(chalk_1.default.yellow(`⚠️ Project structure generation skipped or failed: ${structureRes.error || "unknown"}`));
203
191
  }
204
192
  this.spinner.success({
205
- text: "🎉 All artifacts generated successfully!",
193
+ text: "🎉 Context Scaffold Ready! Your Living Brain is now the perfect map for your next AI Agent.",
206
194
  });
207
195
  // Show next steps for the "all" workflow
208
196
  this.printNextStepsAfterGenerate("all");
@@ -406,7 +394,40 @@ class GenerateCommand {
406
394
  }
407
395
  }
408
396
  catch { }
409
- // 5) Interactive prompt to capture description (when not in --yes)
397
+ // 5) Auto-Inference fallback: Attempt to scan project if no PRD/Types found
398
+ try {
399
+ this.spinner.updateText("🔍 No context found. Inferring project structure...");
400
+ const scanner = new ProjectScanner_1.ProjectScanner(process.cwd());
401
+ const snapshot = await scanner.scan();
402
+ if (snapshot.stats.totalFiles > 5) {
403
+ const fileTree = snapshot.fileTree.filter(f => f.type === 'file').map(f => f.path).slice(0, 50).join('\n');
404
+ const keyFiles = snapshot.keyFiles.map(f => `--- ${f.path} ---\n${f.content}`).join('\n\n');
405
+ const inferenceDescription = `
406
+ AUTO-INFERRED PROJECT ANALYSIS:
407
+ The following project structure was detected. Please analyze it to build a comprehensive Living Brain.
408
+
409
+ STATS:
410
+ - Total Files: ${snapshot.stats.totalFiles}
411
+ - Route Files: ${snapshot.stats.routeFiles}
412
+ - Component Files: ${snapshot.stats.componentFiles}
413
+ - Schema Files: ${snapshot.stats.schemaFiles}
414
+
415
+ FILE TREE (Partial):
416
+ ${fileTree}
417
+
418
+ KEY FILES CONTENT:
419
+ ${keyFiles}
420
+
421
+ Based on this, generate a complete project context including PRD, Technical Specs, and Component Architecture.
422
+ `;
423
+ console.log(chalk_1.default.blue("\n✨ Auto-inferred project context from codebase (Self-Analysis Mode)"));
424
+ return { description: inferenceDescription };
425
+ }
426
+ }
427
+ catch (e) {
428
+ // Fallback to interactive prompts
429
+ }
430
+ // 6) Interactive prompt to capture description (when not in --yes)
410
431
  if (!options.yes) {
411
432
  this.spinner.stop();
412
433
  console.log(chalk_1.default.yellow("\n⚠️ No context files found (.mycontext/01-prd.md or .mycontext/types/)"));
@@ -436,7 +457,7 @@ class GenerateCommand {
436
457
  }
437
458
  this.spinner.start().updateText("Continuing without description...");
438
459
  }
439
- // 6) Fallback to project config
460
+ // 7) Fallback to project config
440
461
  return await this.getProjectContext();
441
462
  }
442
463
  async generateContext(projectContext, options) {
@@ -658,9 +679,11 @@ Use the business entities from the context above, not generic types.`;
658
679
  const hasLocalKeys = this.hasLocalAIKeys();
659
680
  if (hasLocalKeys) {
660
681
  // Use local AI first (user's own keys)
661
- this.spinner.updateText(`🔧 Generating TypeScript types with ${await this.ai.getActiveProviderName()}...`);
682
+ // DEFAULT to gpt-4o-mini for planning tasks to avoid 8k token limits
683
+ const model = options.model || process.env.MYCONTEXT_MODEL || "gpt-4o-mini";
684
+ this.spinner.updateText(`🔧 Generating TypeScript types with ${await this.ai.getActiveProviderName()} (${model})...`);
662
685
  const { text, provider } = await this.ai.generateText(prompt, {
663
- model: options.model || process.env.MYCONTEXT_MODEL,
686
+ model,
664
687
  modelCandidates: this.getModelCandidates(options),
665
688
  spinnerCallback: (text, resetTimer = false) => {
666
689
  this.spinner.updateText(text);
@@ -671,6 +694,13 @@ Use the business entities from the context above, not generic types.`;
671
694
  });
672
695
  // Parse the generated content and create structured files
673
696
  const structuredContent = this.parseAndStructureTypes(text);
697
+ // Update SSOT (Brain)
698
+ await this.updateLivingContext({
699
+ metadata: {
700
+ lastUpdatedAt: new Date().toISOString(),
701
+ status: "types-generated"
702
+ }
703
+ });
674
704
  // Check if AI generated generic types (fallback detection)
675
705
  if ((0, typeTemplateGenerator_1.isGenericTypes)(structuredContent)) {
676
706
  console.log("⚠️ AI generated generic types, using template fallback...");
@@ -923,11 +953,27 @@ Use the business entities from the context above, not generic types.`;
923
953
  }
924
954
  return contextContent;
925
955
  }
956
+ truncateContext(text, maxChars = 4000) {
957
+ if (!text || text.length <= maxChars)
958
+ return text;
959
+ return text.substring(0, maxChars) + "\n\n... [TRUNCATED FOR TOKENS] ...";
960
+ }
926
961
  /**
927
962
  * Unified method to load all context files for consistent discovery
928
963
  */
929
964
  async loadAllContextFiles() {
930
965
  const projectRoot = this.getProjectRoot();
966
+ const livingBrain = await this.loadLivingContext();
967
+ if (livingBrain) {
968
+ return {
969
+ prd: livingBrain.prd.title + "\n" + livingBrain.prd.problemStatement,
970
+ features: livingBrain.features.map(f => `${f.name}: ${f.description}`).join("\n"),
971
+ userFlows: livingBrain.flows.map(f => `${f.name}: ${f.steps.join(", ")}`).join("\n"),
972
+ edgeCases: livingBrain.edgeCases.map(e => `${e.category}: ${e.description}`).join("\n"),
973
+ technicalSpecs: livingBrain.specs.architecture,
974
+ hasContext: true,
975
+ };
976
+ }
931
977
  const contextDir = path_1.default.join(projectRoot, ".mycontext");
932
978
  const readOrEmpty = async (filePath) => {
933
979
  if (await fs.pathExists(filePath)) {
@@ -1774,9 +1820,11 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
1774
1820
  const hasLocalKeys = this.hasLocalAIKeys();
1775
1821
  if (hasLocalKeys) {
1776
1822
  // Use local AI first (user's own keys)
1777
- this.spinner.updateText(`🎨 Generating brand system with ${await this.ai.getActiveProviderName()}...`);
1823
+ // DEFAULT to gpt-4o-mini for planning tasks to avoid 8k token limits
1824
+ const model = options.model || process.env.MYCONTEXT_MODEL || "gpt-4o-mini";
1825
+ this.spinner.updateText(`🎨 Generating brand system with ${await this.ai.getActiveProviderName()} (${model})...`);
1778
1826
  const { text, provider } = await this.ai.generateText(prompt, {
1779
- model: options.model || process.env.MYCONTEXT_MODEL,
1827
+ model,
1780
1828
  modelCandidates: this.getModelCandidates(options),
1781
1829
  spinnerCallback: (text, resetTimer = false) => {
1782
1830
  this.spinner.updateText(text);
@@ -1787,6 +1835,13 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
1787
1835
  });
1788
1836
  // Parse and create the brand system files
1789
1837
  const brandFiles = this.parseAndCreateBrandSystem(text);
1838
+ // Update SSOT (Brain)
1839
+ await this.updateLivingContext({
1840
+ metadata: {
1841
+ lastUpdatedAt: new Date().toISOString(),
1842
+ status: "brand-generated"
1843
+ }
1844
+ });
1790
1845
  return {
1791
1846
  success: true,
1792
1847
  content: brandFiles.guide,
@@ -1891,23 +1946,37 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
1891
1946
  }
1892
1947
  }
1893
1948
  async generateComponentList(projectContext, options) {
1949
+ // 1. Try to load from Living Brain first
1950
+ const livingContext = await this.loadLivingContext();
1951
+ if (livingContext && livingContext.components && livingContext.components.length > 0 && !options.force) {
1952
+ this.spinner.updateText("📋 Using component architecture from Living Brain...");
1953
+ return {
1954
+ success: true,
1955
+ content: JSON.stringify(livingContext.components, null, 2),
1956
+ provider: "local",
1957
+ metadata: {
1958
+ model: "static",
1959
+ tokens: 0,
1960
+ latency: 0,
1961
+ },
1962
+ };
1963
+ }
1964
+ // 2. Fallback to AI generation if brain is empty or force is used
1965
+ this.spinner.updateText("📋 Discovering component architecture via AI...");
1894
1966
  // Load comprehensive context for better component generation
1895
1967
  const allContext = await this.loadAllContextFiles();
1896
1968
  const ctx = await this.readContextArtifacts();
1897
- // Note: No core readiness check here - components-list generation should happen
1898
- // before any core component is selected and refined
1899
1969
  const coreExcerpt = await this.readCoreExcerpt();
1970
+ // DEFAULT to gpt-4o-mini for planning tasks to avoid 8k token limits
1971
+ const model = options.model || process.env.MYCONTEXT_MODEL || "gpt-4o-mini";
1900
1972
  const prompt = [
1901
- `[mycontext] Plan: plan generate QA docs preview (→ checks)`,
1902
- `Create a detailed, business-specific component list for: ${projectContext.description || "MyContext project"}`,
1903
- "",
1904
- "IMPORTANT: Use the detailed business context below to create components that directly implement the specific features, user stories, and acceptance criteria described. Extract exact feature names, user roles, and business requirements to generate precise, domain-specific components.",
1973
+ `[mycontext] Planning: Identify and decompose components for: ${projectContext.description || "MyContext project"}`,
1905
1974
  "",
1906
- "Example: If the context mentions 'Mobile Order Entry' with specific acceptance criteria like 'Given a front office user is logged in, When they enter an order via a mobile device, Then the order should be saved and synced to the backend', create a component like 'MobileOrderEntry' with a description that references these specific requirements.",
1975
+ "IMPORTANT: Use the detailed business context below to create components that directly implement the specific features described. Group components by domain (e.g., Auth, Dashboard, Billing).",
1907
1976
  "",
1908
1977
  ...(coreExcerpt
1909
1978
  ? [
1910
- "Core component excerpt (use for visual/style consistency):",
1979
+ "Core component style (use for reference):",
1911
1980
  coreExcerpt,
1912
1981
  "",
1913
1982
  ]
@@ -1915,90 +1984,38 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
1915
1984
  "Business Context:",
1916
1985
  allContext.hasContext
1917
1986
  ? [
1918
- allContext.prd ? `## PRD\n${allContext.prd}` : "",
1919
- allContext.features ? `## Features\n${allContext.features}` : "",
1920
- allContext.userFlows
1921
- ? `## User Flows\n${allContext.userFlows}`
1922
- : "",
1923
- allContext.edgeCases
1924
- ? `## Edge Cases\n${allContext.edgeCases}`
1925
- : "",
1926
- allContext.technicalSpecs
1927
- ? `## Technical Specs\n${allContext.technicalSpecs}`
1928
- : "",
1987
+ allContext.prd ? `## PRD\n${this.truncateContext(allContext.prd)}` : "",
1988
+ allContext.features ? `## Features\n${this.truncateContext(allContext.features)}` : "",
1989
+ allContext.userFlows ? `## User Flows\n${this.truncateContext(allContext.userFlows)}` : "",
1990
+ allContext.technicalSpecs ? `## Technical Specs\n${this.truncateContext(allContext.technicalSpecs)}` : "",
1929
1991
  ]
1930
1992
  .filter(Boolean)
1931
1993
  .join("\n\n")
1932
- : ctx.prd.split("\n").slice(0, 60).join("\n"),
1994
+ : this.truncateContext(ctx.prd, 2000),
1933
1995
  "",
1934
- "Types excerpt:",
1935
- ctx.types.split("\n").slice(0, 80).join("\n"),
1996
+ "Types Summary:",
1997
+ this.truncateContext(ctx.types, 3000),
1936
1998
  "",
1937
- "Branding excerpt:",
1938
- ctx.brand.split("\n").slice(0, 40).join("\n"),
1999
+ "Branding Summary:",
2000
+ this.truncateContext(ctx.brand, 1500),
1939
2001
  "",
1940
- "Available shadcn/ui primitives (import from '@/components/ui/<component>'):",
2002
+ "Available shadcn/ui primitives:",
1941
2003
  (ctx.shadcn.length ? ctx.shadcn : this.getCanonicalShadcnList())
1942
2004
  .slice(0, 60)
1943
2005
  .join(", "),
1944
2006
  "",
1945
- "Return strictly valid JSON ONLY with this HIERARCHICAL structure:",
2007
+ "Return strictly valid JSON with this structure:",
1946
2008
  "{",
1947
- ' "ApplicationName": {',
1948
- ' "description": "Main application component",',
1949
- ' "progress": { "completed": 0, "total": 0 },',
1950
- ' "children": {',
1951
- ' "Header": {',
1952
- ' "description": "Application header section",',
1953
- ' "progress": { "completed": 0, "total": 0 },',
1954
- ' "children": {',
1955
- ' "Logo": { "description": "Company logo", "type": "display" },',
1956
- ' "Navigation": { "description": "Main navigation", "type": "interactive" }',
1957
- " }",
1958
- " },",
1959
- ' "Main": {',
1960
- ' "description": "Main content area",',
1961
- ' "progress": { "completed": 0, "total": 0 },',
1962
- ' "children": {',
1963
- ' "BusinessSection1": {',
1964
- ' "description": "First business domain section",',
1965
- ' "progress": { "completed": 0, "total": 0 },',
1966
- ' "children": {',
1967
- ' "SubComponent1": { "description": "Specific business component", "type": "form" },',
1968
- ' "SubComponent2": { "description": "Another business component", "type": "display" }',
1969
- " }",
1970
- " }",
1971
- " }",
1972
- " }",
1973
- " }",
1974
- " },",
1975
- ' "metadata": {',
1976
- ' "coreCandidates": [',
1977
- ' { "name": string, "path": string, "reason": string },',
1978
- ' { "name": string, "path": string, "reason": string }',
1979
- " ],",
1980
- ' "totalComponents": 0,',
1981
- ' "completedComponents": 0',
1982
- " }",
2009
+ ' "components": [',
2010
+ ' { "name": "ComponentName", "description": "Specific purpose", "type": "form|layout|display|interactive", "group": "GroupName", "status": "planned" }',
2011
+ " ],",
2012
+ ' "metadata": { "totalComponents": 0 }',
1983
2013
  "}",
1984
2014
  "",
1985
2015
  "Rules:",
1986
- "- Create a HIERARCHICAL component structure that mirrors React component composition",
1987
- "- Start with the main application component as the root",
1988
- "- Nest components from largest to smallest (App -> Sections -> SubComponents -> Atomic Components)",
1989
- "- Each component should have: description, progress tracking, and children (if any)",
1990
- "- Progress tracking: { completed: 0, total: 0 } - total counts all nested components",
1991
- "- CRITICAL: Use the SPECIFIC business context above to generate detailed, domain-specific components",
1992
- "- Extract specific feature names, user stories, and acceptance criteria from the context",
1993
- "- Create components that directly implement the features described in the context",
1994
- "- Use the exact terminology and business language from the context",
1995
- "- Group by business domain in the hierarchy (e.g., 'OrderManagement', 'InventoryManagement')",
1996
- "- Each leaf component should have a 'type' field: 'layout', 'display', 'interactive', 'form'",
1997
- "- Component descriptions should reference specific business requirements from the context",
1998
- "- No code fences, no comments, no trailing commas.",
1999
- "- Use only the fields above.",
2000
- "- Ensure arrays/objects have no extra commas.",
2001
- "- Provide 2-3 'coreCandidates' with their full path in the hierarchy.",
2016
+ "- Logical React composition starting from root",
2017
+ "- Extract specific feature terminology from context",
2018
+ "- Return ONLY JSON - no markdown fences.",
2002
2019
  ].join("\n");
2003
2020
  try {
2004
2021
  // Check if user has local AI keys configured
@@ -2007,10 +2024,9 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
2007
2024
  let provider;
2008
2025
  if (hasLocalKeys) {
2009
2026
  // Use local AI first (user's own keys)
2010
- this.spinner.updateText(`📋 Generating component list with ${await this.ai.getActiveProviderName()}...`);
2011
2027
  try {
2012
2028
  const r = await this.ai.generateText(prompt, {
2013
- model: options.model || process.env.MYCONTEXT_MODEL,
2029
+ model,
2014
2030
  modelCandidates: this.getModelCandidates(options),
2015
2031
  spinnerCallback: (text, resetTimer = false) => {
2016
2032
  this.spinner.updateText(text);
@@ -2023,12 +2039,12 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
2023
2039
  provider = r.provider;
2024
2040
  }
2025
2041
  catch (e) {
2026
- // Handle transient network issues like unexpected EOF with a single retry
2042
+ // Retry logic (existing)
2027
2043
  const msg = String(e?.message || e);
2028
2044
  if (/unexpected EOF|ECONNRESET|EPIPE/i.test(msg)) {
2029
- this.spinner.updateText("Retrying component list generation after transient error...");
2045
+ this.spinner.updateText("Retrying component list generation...");
2030
2046
  const r2 = await this.ai.generateText(prompt, {
2031
- model: options.model || process.env.MYCONTEXT_MODEL,
2047
+ model,
2032
2048
  modelCandidates: this.getModelCandidates(options),
2033
2049
  spinnerCallback: (text, resetTimer = false) => {
2034
2050
  this.spinner.updateText(text);
@@ -2047,7 +2063,6 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
2047
2063
  }
2048
2064
  else {
2049
2065
  // No local keys - use hosted API (requires authentication)
2050
- this.spinner.updateText("📋 Generating component list with MyContext AI (hosted)...");
2051
2066
  const hostedResult = await this.hostedApi.generateContext("components-list", prompt, {
2052
2067
  model: options.model || "mycontext",
2053
2068
  context: projectContext,
@@ -2063,6 +2078,21 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
2063
2078
  // Attempt to repair and extract valid JSON
2064
2079
  const repaired = this.repairJson(text);
2065
2080
  const cleanedContent = this.extractJson(repaired);
2081
+ // Update SSOT (Brain)
2082
+ try {
2083
+ const obj = JSON.parse(cleanedContent);
2084
+ await this.updateLivingContext({
2085
+ metadata: {
2086
+ lastUpdatedAt: new Date().toISOString(),
2087
+ status: "components-planned"
2088
+ }
2089
+ // Note: Full component hierarchy is heavy, keep in 04-component-list.json for now
2090
+ // or we could flatten and store in context.json.components
2091
+ });
2092
+ }
2093
+ catch (e) {
2094
+ // ignore parse errors for SSOT update; saveGeneratedContent will handle it later
2095
+ }
2066
2096
  // Attach rich context so JSON alone is enough for component generation
2067
2097
  try {
2068
2098
  console.log(`[GenerateCommand] Attempting to parse cleaned content (first 200 chars): ${cleanedContent.slice(0, 200)}...`);
@@ -3529,35 +3559,128 @@ export function Typography({ variant, children, className }: TypographyProps) {
3529
3559
  // 5) Fallback: wrap into minimal valid envelope
3530
3560
  return JSON.stringify({ error: "Invalid JSON from AI", raw: raw.slice(0, 500) }, null, 2);
3531
3561
  }
3562
+ async loadLivingContext() {
3563
+ try {
3564
+ const contextPath = path_1.default.join(this.getProjectRoot(), ".mycontext", "context.json");
3565
+ if (await fs.pathExists(contextPath)) {
3566
+ return await fs.readJson(contextPath);
3567
+ }
3568
+ }
3569
+ catch (e) {
3570
+ // ignore
3571
+ }
3572
+ return null;
3573
+ }
3574
+ async updateLivingContext(update) {
3575
+ try {
3576
+ const projectRoot = this.getProjectRoot();
3577
+ const contextPath = path_1.default.join(projectRoot, ".mycontext", "context.json");
3578
+ let current = await this.loadLivingContext();
3579
+ if (!current) {
3580
+ // Fallback to empty context if it doesn't exist
3581
+ current = {
3582
+ metadata: {
3583
+ version: "1.0.0",
3584
+ generatedAt: new Date().toISOString(),
3585
+ lastUpdatedAt: new Date().toISOString(),
3586
+ projectConfig: {
3587
+ id: require("crypto").randomUUID(),
3588
+ name: "MyContext Project",
3589
+ description: "",
3590
+ createdAt: new Date().toISOString(),
3591
+ updatedAt: new Date().toISOString(),
3592
+ contextPath: ".mycontext",
3593
+ version: "0.1.0",
3594
+ status: "initialized"
3595
+ }
3596
+ },
3597
+ prd: { title: "", problemStatement: "", goals: [], targetAudience: "", successMetrics: [] },
3598
+ features: [],
3599
+ flows: [],
3600
+ edgeCases: [],
3601
+ specs: {
3602
+ architecture: "",
3603
+ techStack: { frontend: [], backend: [], database: [], other: [] },
3604
+ apiEndpoints: [],
3605
+ databaseSchema: { tables: [] }
3606
+ },
3607
+ components: [],
3608
+ actions: [],
3609
+ routes: [],
3610
+ brain: { memory: {}, logs: [] }
3611
+ };
3612
+ }
3613
+ const { deepMerge } = await Promise.resolve().then(() => __importStar(require("../utils/deepMerge")));
3614
+ const merged = deepMerge(current, update);
3615
+ merged.metadata.lastUpdatedAt = new Date().toISOString();
3616
+ await fs.writeJson(contextPath, merged, { spaces: 2 });
3617
+ }
3618
+ catch (e) {
3619
+ console.log(chalk_1.default.yellow(`⚠️ Failed to update context.json: ${e instanceof Error ? e.message : String(e)}`));
3620
+ }
3621
+ }
3532
3622
  async readContextArtifacts() {
3533
3623
  try {
3534
- const cwd = process.cwd();
3535
- const readOrEmpty = async (rel) => (await fs.pathExists(path_1.default.join(cwd, rel)))
3536
- ? await fs.readFile(path_1.default.join(cwd, rel), "utf8")
3537
- : "";
3538
- // Prefer split files if present
3624
+ const cwd = this.getProjectRoot();
3625
+ const livingBrain = await this.loadLivingContext();
3539
3626
  let prd = "";
3540
- const brief = (await readOrEmpty(".mycontext/01a-brief.md")) ||
3541
- (await readOrEmpty(".mycontext/01-brief.md"));
3542
- const reqs = (await readOrEmpty(".mycontext/01b-requirements.md")) ||
3543
- (await readOrEmpty(".mycontext/02-requirements.md"));
3544
- const flows = (await readOrEmpty(".mycontext/01c-flows.md")) ||
3545
- (await readOrEmpty(".mycontext/03-flows.md"));
3546
- if (brief || reqs || flows) {
3627
+ let types = "";
3628
+ let brand = "";
3629
+ if (livingBrain) {
3630
+ // Build PRD string from JSON
3547
3631
  prd = [
3548
- brief ? "## Brief\n\n" + brief : "",
3549
- reqs ? "\n\n## Requirements\n\n" + reqs : "",
3550
- flows ? "\n\n## Flows\n\n" + flows : "",
3551
- ]
3552
- .filter(Boolean)
3553
- .join("");
3632
+ `# ${livingBrain.prd.title}`,
3633
+ `## Problem Statement\n${livingBrain.prd.problemStatement}`,
3634
+ `## Goals\n${livingBrain.prd.goals.map(g => `- ${g}`).join("\n")}`,
3635
+ livingBrain.features.length ? `## Features\n${livingBrain.features.map(f => `### ${f.name} (${f.priority})\n${f.description}`).join("\n\n")}` : "",
3636
+ livingBrain.flows.length ? `## Flows\n${livingBrain.flows.map(f => `### ${f.name}\n${f.steps.join("\n")}`).join("\n\n")}` : ""
3637
+ ].filter(Boolean).join("\n\n");
3554
3638
  }
3639
+ const readOrEmpty = async (rel) => (await fs.pathExists(path_1.default.join(cwd, rel)))
3640
+ ? await fs.readFile(path_1.default.join(cwd, rel), "utf8")
3641
+ : "";
3642
+ // Fallback/Augment PRD from files if JSON is sparse
3555
3643
  if (!prd) {
3556
- prd = await readOrEmpty(".mycontext/01-prd.md");
3644
+ const brief = (await readOrEmpty(".mycontext/01a-brief.md")) || (await readOrEmpty(".mycontext/01-brief.md"));
3645
+ const reqs = (await readOrEmpty(".mycontext/01b-requirements.md")) || (await readOrEmpty(".mycontext/02-requirements.md"));
3646
+ const flows = (await readOrEmpty(".mycontext/01c-flows.md")) || (await readOrEmpty(".mycontext/03-flows.md"));
3647
+ if (brief || reqs || flows) {
3648
+ prd = [
3649
+ brief ? "## Brief\n\n" + brief : "",
3650
+ reqs ? "\n\n## Requirements\n\n" + reqs : "",
3651
+ flows ? "\n\n## Flows\n\n" + flows : "",
3652
+ ].filter(Boolean).join("");
3653
+ }
3654
+ if (!prd) {
3655
+ prd = await readOrEmpty(".mycontext/01-prd.md");
3656
+ }
3657
+ }
3658
+ // Discover Types (Modern Multi-file)
3659
+ const typesDir = path_1.default.join(cwd, ".mycontext", "types");
3660
+ if (await fs.pathExists(typesDir)) {
3661
+ const files = await fs.readdir(typesDir);
3662
+ const typeFiles = files.filter(f => f.endsWith(".ts"));
3663
+ for (const file of typeFiles) {
3664
+ const content = await fs.readFile(path_1.default.join(typesDir, file), "utf8");
3665
+ types += `\n// --- ${file} ---\n${content}\n`;
3666
+ }
3667
+ }
3668
+ if (!types) {
3669
+ types = (await readOrEmpty(".mycontext/02-types.ts")) || (await readOrEmpty(".mycontext/02-types-guide.md"));
3670
+ }
3671
+ // Discover Brand (Modern Multi-file)
3672
+ const brandDir = path_1.default.join(cwd, ".mycontext", "brand");
3673
+ if (await fs.pathExists(brandDir)) {
3674
+ brand = await readOrEmpty(".mycontext/brand/globals.css");
3675
+ const brandFiles = await fs.readdir(brandDir);
3676
+ for (const file of brandFiles.filter(f => f.endsWith(".md"))) {
3677
+ const content = await fs.readFile(path_1.default.join(brandDir, file), "utf8");
3678
+ brand += `\n\n### ${file}\n${content}`;
3679
+ }
3680
+ }
3681
+ if (!brand) {
3682
+ brand = (await readOrEmpty(".mycontext/03-branding.md")) || (await readOrEmpty(".mycontext/03-branding-guide.md"));
3557
3683
  }
3558
- const types = await readOrEmpty(".mycontext/02-types.ts");
3559
- const brand = (await readOrEmpty(".mycontext/brand/globals.css")) ||
3560
- (await readOrEmpty(".mycontext/03-branding.md"));
3561
3684
  // discover shadcn/ui primitives from components/ui
3562
3685
  const uiDir = path_1.default.join(cwd, "components", "ui");
3563
3686
  let shadcn = [];
@@ -3805,11 +3928,11 @@ ${isEcommerce
3805
3928
  console.log(chalk_1.default.gray(" Visit /preview (dev server)"));
3806
3929
  break;
3807
3930
  case "all":
3808
- console.log(chalk_1.default.blue("\n🎉 Full context generated! Next steps:"));
3809
- console.log(chalk_1.default.gray(" 1. Review generated files in .mycontext/"));
3810
- console.log(chalk_1.default.gray(" 2. Generate components: mycontext generate-components all"));
3811
- console.log(chalk_1.default.gray(" 3. Start development: npm run dev"));
3812
- console.log(chalk_1.default.gray(" 4. Preview: Visit /preview (dev server)"));
3931
+ console.log(chalk_1.default.blue("\n🚀 Context Scaffolding Complete! Next steps:"));
3932
+ console.log(chalk_1.default.gray(" 1. Open .mycontext/ to see your project's new Living Brain."));
3933
+ console.log(chalk_1.default.gray(" 2. Recommended: Hand off .mycontext/ to your favorite AI agent (e.g. Antigravity)."));
3934
+ console.log(chalk_1.default.gray(" 3. Start building: npm run dev"));
3935
+ console.log(chalk_1.default.gray(" 4. Preview architectural alignment: Visit /preview"));
3813
3936
  break;
3814
3937
  default:
3815
3938
  break;
@@ -3836,35 +3959,7 @@ ${isEcommerce
3836
3959
  return aliases[type] || type;
3837
3960
  }
3838
3961
  hasLocalAIKeys() {
3839
- // Check for any local AI provider keys
3840
- const keys = {
3841
- github: !!process.env.MYCONTEXT_GITHUB_TOKEN,
3842
- qwen: !!process.env.MYCONTEXT_QWEN_API_KEY,
3843
- gemini: !!(process.env.GEMINI_API_KEY ||
3844
- process.env.GOOGLE_API_KEY ||
3845
- process.env.MYCONTEXT_GEMINI_API_KEY),
3846
- xai: !!(process.env.MYCONTEXT_XAI_API_KEY || process.env.XAI_API_KEY),
3847
- claude: !!process.env.MYCONTEXT_CLAUDE_API_KEY,
3848
- openai: !!process.env.OPENAI_API_KEY,
3849
- anthropic: !!process.env.ANTHROPIC_API_KEY,
3850
- huggingface: !!process.env.HUGGINGFACE_API_KEY,
3851
- openrouter: !!(process.env.MYCONTEXT_OPENROUTER_API_KEY ||
3852
- process.env.OPENROUTER_API_KEY),
3853
- };
3854
- console.log(`[GenerateCommand] API Keys detected:`, keys);
3855
- return !!(process.env.MYCONTEXT_GITHUB_TOKEN ||
3856
- process.env.MYCONTEXT_QWEN_API_KEY ||
3857
- process.env.GEMINI_API_KEY ||
3858
- process.env.GOOGLE_API_KEY ||
3859
- process.env.MYCONTEXT_GEMINI_API_KEY ||
3860
- process.env.MYCONTEXT_XAI_API_KEY ||
3861
- process.env.XAI_API_KEY ||
3862
- process.env.MYCONTEXT_CLAUDE_API_KEY ||
3863
- process.env.OPENAI_API_KEY ||
3864
- process.env.ANTHROPIC_API_KEY ||
3865
- process.env.HUGGINGFACE_API_KEY ||
3866
- process.env.MYCONTEXT_OPENROUTER_API_KEY ||
3867
- process.env.OPENROUTER_API_KEY);
3962
+ return AICore_1.AICore.getInstance().hasAnyProvider();
3868
3963
  }
3869
3964
  getModelCandidates(options) {
3870
3965
  const raw = options.modelCandidates ||
@@ -3956,7 +4051,8 @@ INSTRUCTIONS:
3956
4051
  1. Analyze the project details provided above (which may include file structure, tech stack, and README summary).
3957
4052
  2. Extract functional requirements, user flows, and technical limits.
3958
4053
  3. If the description includes a file tree, infer the project architecture from it.
3959
- 4. Generate a complete PRD and Technical Spec that reflects this ACTUAL existing project.
4054
+ 4. Identify all necessary UI components and group them logically (e.g. Auth, Sidebar, Dashboard).
4055
+ 5. Generate a complete PRD, Technical Spec, and Component Architecture that reflects this ACTUAL existing project.
3960
4056
  `;
3961
4057
  }
3962
4058
  // The schema for LivingContext (simplified version for the LLM)
@@ -3966,12 +4062,31 @@ INSTRUCTIONS:
3966
4062
  "features": [{ "id": "...", "name": "...", "description": "...", "priority": "high|medium|low", "userValue": "...", "acceptanceCriteria": [], "dependencies": [] }],
3967
4063
  "flows": [{ "id": "...", "name": "...", "description": "...", "steps": [], "actors": [] }],
3968
4064
  "edgeCases": [{ "id": "...", "category": "...", "description": "...", "mitigation": "..." }],
4065
+ "brand": {
4066
+ "theme": "light|dark",
4067
+ "colors": { "primary": "#...", "background": "#...", "text": "#...", "accent": "#..." },
4068
+ "typography": { "fontFamily": "...", "headingScale": "..." },
4069
+ "designPrinciples": []
4070
+ },
4071
+ "types": {
4072
+ "entities": [{ "name": "...", "description": "...", "schema": "export interface ... { ... }" }],
4073
+ "shared": [{ "name": "...", "description": "...", "schema": "export interface ... { ... }" }]
4074
+ },
3969
4075
  "specs": {
3970
4076
  "architecture": "...",
3971
4077
  "techStack": { "frontend": [], "backend": [], "database": [], "other": [] },
3972
4078
  "apiEndpoints": [{ "path": "...", "method": "...", "description": "...", "authRequired": true }],
3973
4079
  "databaseSchema": { "tables": [{ "name": "...", "columns": [{ "name": "...", "type": "...", "constraints": [] }] }] }
3974
- }
4080
+ },
4081
+ "components": [
4082
+ {
4083
+ "name": "ComponentName",
4084
+ "description": "...",
4085
+ "type": "form|layout|display|interactive",
4086
+ "group": "Auth|Dashboard|etc",
4087
+ "status": "planned"
4088
+ }
4089
+ ]
3975
4090
  }
3976
4091
  `;
3977
4092
  const livingContextData = await aiCore.generateStructuredText(prompt, schema);
@@ -4019,6 +4134,9 @@ INSTRUCTIONS:
4019
4134
  { name: "01b-user-flows.md", content: contextRenderer_1.ContextRenderer.renderUserFlows(livingContext) },
4020
4135
  { name: "01c-edge-cases.md", content: contextRenderer_1.ContextRenderer.renderEdgeCases(livingContext) },
4021
4136
  { name: "01d-technical-specs.md", content: contextRenderer_1.ContextRenderer.renderTechnicalSpecs(livingContext) },
4137
+ { name: "02-types-guide.md", content: contextRenderer_1.ContextRenderer.renderTypesGuide(livingContext) },
4138
+ { name: "03-brand-guide.md", content: contextRenderer_1.ContextRenderer.renderBrandGuide(livingContext) },
4139
+ { name: "04-component-list.md", content: contextRenderer_1.ContextRenderer.renderComponentList(livingContext) },
4022
4140
  ];
4023
4141
  for (const render of renders) {
4024
4142
  const filePath = path_1.default.join(mycontextDir, render.name);
@@ -4030,7 +4148,7 @@ INSTRUCTIONS:
4030
4148
  this.spinner.success({ text: "🎉 Living Context generated and exported!" });
4031
4149
  return {
4032
4150
  success: true,
4033
- content: JSON.stringify(livingContext, null, 2),
4151
+ content: contextRenderer_1.ContextRenderer.renderPRD(livingContext),
4034
4152
  provider: "hybrid",
4035
4153
  metadata: { model: "structured", tokens: 0, latency: 0 },
4036
4154
  };