mycontext-cli 4.2.15 → 4.2.17

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 (77) hide show
  1. package/README.md +39 -38
  2. package/dist/cli.js +15 -44
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/generate-components.d.ts.map +1 -1
  5. package/dist/commands/generate-components.js +21 -18
  6. package/dist/commands/generate-components.js.map +1 -1
  7. package/dist/commands/generate.d.ts +3 -0
  8. package/dist/commands/generate.d.ts.map +1 -1
  9. package/dist/commands/generate.js +242 -158
  10. package/dist/commands/generate.js.map +1 -1
  11. package/dist/commands/init-interactive.d.ts.map +1 -1
  12. package/dist/commands/init-interactive.js +30 -2
  13. package/dist/commands/init-interactive.js.map +1 -1
  14. package/dist/commands/init.d.ts +1 -0
  15. package/dist/commands/init.d.ts.map +1 -1
  16. package/dist/commands/init.js +194 -38
  17. package/dist/commands/init.js.map +1 -1
  18. package/dist/commands/setup-complete.d.ts.map +1 -1
  19. package/dist/commands/setup-complete.js +4 -6
  20. package/dist/commands/setup-complete.js.map +1 -1
  21. package/dist/commands/workflow.d.ts.map +1 -1
  22. package/dist/commands/workflow.js +10 -58
  23. package/dist/commands/workflow.js.map +1 -1
  24. package/dist/core/ai/AICore.d.ts +4 -0
  25. package/dist/core/ai/AICore.d.ts.map +1 -1
  26. package/dist/core/ai/AICore.js +6 -0
  27. package/dist/core/ai/AICore.js.map +1 -1
  28. package/dist/tui/DashboardMode.d.ts +14 -0
  29. package/dist/tui/DashboardMode.d.ts.map +1 -0
  30. package/dist/tui/DashboardMode.js +75 -0
  31. package/dist/tui/DashboardMode.js.map +1 -0
  32. package/dist/tui/TUIClient.d.ts +9 -0
  33. package/dist/tui/TUIClient.d.ts.map +1 -1
  34. package/dist/tui/TUIClient.js +63 -0
  35. package/dist/tui/TUIClient.js.map +1 -1
  36. package/dist/types/index.d.ts +1 -1
  37. package/dist/types/index.d.ts.map +1 -1
  38. package/dist/types/living-context.d.ts +1 -0
  39. package/dist/types/living-context.d.ts.map +1 -1
  40. package/dist/types/tui.d.ts +3 -1
  41. package/dist/types/tui.d.ts.map +1 -1
  42. package/dist/utils/contextEnricher.d.ts +2 -1
  43. package/dist/utils/contextEnricher.d.ts.map +1 -1
  44. package/dist/utils/contextEnricher.js +138 -1
  45. package/dist/utils/contextEnricher.js.map +1 -1
  46. package/dist/utils/contextRenderer.d.ts +3 -0
  47. package/dist/utils/contextRenderer.d.ts.map +1 -1
  48. package/dist/utils/contextRenderer.js +65 -0
  49. package/dist/utils/contextRenderer.js.map +1 -1
  50. package/dist/utils/fileSystem.d.ts.map +1 -1
  51. package/dist/utils/fileSystem.js +32 -1
  52. package/dist/utils/fileSystem.js.map +1 -1
  53. package/dist/utils/geminiClient.d.ts.map +1 -1
  54. package/dist/utils/geminiClient.js +10 -5
  55. package/dist/utils/geminiClient.js.map +1 -1
  56. package/dist/utils/githubModelsClient.d.ts.map +1 -1
  57. package/dist/utils/githubModelsClient.js +4 -1
  58. package/dist/utils/githubModelsClient.js.map +1 -1
  59. package/dist/utils/openRouterClient.d.ts.map +1 -1
  60. package/dist/utils/openRouterClient.js +1 -0
  61. package/dist/utils/openRouterClient.js.map +1 -1
  62. package/dist/utils/unifiedDesignContextLoader.d.ts.map +1 -1
  63. package/dist/utils/unifiedDesignContextLoader.js +14 -0
  64. package/dist/utils/unifiedDesignContextLoader.js.map +1 -1
  65. package/package.json +4 -2
  66. package/dist/commands/assemble-features.d.ts +0 -40
  67. package/dist/commands/assemble-features.d.ts.map +0 -1
  68. package/dist/commands/assemble-features.js +0 -383
  69. package/dist/commands/assemble-features.js.map +0 -1
  70. package/dist/commands/compile-prd.d.ts +0 -18
  71. package/dist/commands/compile-prd.d.ts.map +0 -1
  72. package/dist/commands/compile-prd.js +0 -253
  73. package/dist/commands/compile-prd.js.map +0 -1
  74. package/dist/commands/generate-context-files.d.ts +0 -44
  75. package/dist/commands/generate-context-files.d.ts.map +0 -1
  76. package/dist/commands/generate-context-files.js +0 -871
  77. package/dist/commands/generate-context-files.js.map +0 -1
@@ -90,29 +90,16 @@ class GenerateCommand {
90
90
  case "context":
91
91
  case "prd":
92
92
  case "requirements":
93
+ // Check if user wants full context generation (PRD + A/B/C/D files)
93
94
  // Check if user wants full context generation (PRD + A/B/C/D files)
94
95
  if (options.full) {
95
96
  // Generate both PRD and A/B/C/D files
96
97
  result = await this.generateFullContext(projectContext, options);
97
98
  }
98
99
  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
- };
100
+ // Default: Direct to full generation as the new standard
101
+ console.log(chalk_1.default.blue("ℹ️ Note: 'generate context' now defaults to full brain synchronization."));
102
+ result = await this.generateFullContext(projectContext, options);
116
103
  }
117
104
  break;
118
105
  case "types":
@@ -658,9 +645,11 @@ Use the business entities from the context above, not generic types.`;
658
645
  const hasLocalKeys = this.hasLocalAIKeys();
659
646
  if (hasLocalKeys) {
660
647
  // Use local AI first (user's own keys)
661
- this.spinner.updateText(`🔧 Generating TypeScript types with ${await this.ai.getActiveProviderName()}...`);
648
+ // DEFAULT to gpt-4o-mini for planning tasks to avoid 8k token limits
649
+ const model = options.model || process.env.MYCONTEXT_MODEL || "gpt-4o-mini";
650
+ this.spinner.updateText(`🔧 Generating TypeScript types with ${await this.ai.getActiveProviderName()} (${model})...`);
662
651
  const { text, provider } = await this.ai.generateText(prompt, {
663
- model: options.model || process.env.MYCONTEXT_MODEL,
652
+ model,
664
653
  modelCandidates: this.getModelCandidates(options),
665
654
  spinnerCallback: (text, resetTimer = false) => {
666
655
  this.spinner.updateText(text);
@@ -671,6 +660,13 @@ Use the business entities from the context above, not generic types.`;
671
660
  });
672
661
  // Parse the generated content and create structured files
673
662
  const structuredContent = this.parseAndStructureTypes(text);
663
+ // Update SSOT (Brain)
664
+ await this.updateLivingContext({
665
+ metadata: {
666
+ lastUpdatedAt: new Date().toISOString(),
667
+ status: "types-generated"
668
+ }
669
+ });
674
670
  // Check if AI generated generic types (fallback detection)
675
671
  if ((0, typeTemplateGenerator_1.isGenericTypes)(structuredContent)) {
676
672
  console.log("⚠️ AI generated generic types, using template fallback...");
@@ -923,11 +919,27 @@ Use the business entities from the context above, not generic types.`;
923
919
  }
924
920
  return contextContent;
925
921
  }
922
+ truncateContext(text, maxChars = 4000) {
923
+ if (!text || text.length <= maxChars)
924
+ return text;
925
+ return text.substring(0, maxChars) + "\n\n... [TRUNCATED FOR TOKENS] ...";
926
+ }
926
927
  /**
927
928
  * Unified method to load all context files for consistent discovery
928
929
  */
929
930
  async loadAllContextFiles() {
930
931
  const projectRoot = this.getProjectRoot();
932
+ const livingBrain = await this.loadLivingContext();
933
+ if (livingBrain) {
934
+ return {
935
+ prd: livingBrain.prd.title + "\n" + livingBrain.prd.problemStatement,
936
+ features: livingBrain.features.map(f => `${f.name}: ${f.description}`).join("\n"),
937
+ userFlows: livingBrain.flows.map(f => `${f.name}: ${f.steps.join(", ")}`).join("\n"),
938
+ edgeCases: livingBrain.edgeCases.map(e => `${e.category}: ${e.description}`).join("\n"),
939
+ technicalSpecs: livingBrain.specs.architecture,
940
+ hasContext: true,
941
+ };
942
+ }
931
943
  const contextDir = path_1.default.join(projectRoot, ".mycontext");
932
944
  const readOrEmpty = async (filePath) => {
933
945
  if (await fs.pathExists(filePath)) {
@@ -1774,9 +1786,11 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
1774
1786
  const hasLocalKeys = this.hasLocalAIKeys();
1775
1787
  if (hasLocalKeys) {
1776
1788
  // Use local AI first (user's own keys)
1777
- this.spinner.updateText(`🎨 Generating brand system with ${await this.ai.getActiveProviderName()}...`);
1789
+ // DEFAULT to gpt-4o-mini for planning tasks to avoid 8k token limits
1790
+ const model = options.model || process.env.MYCONTEXT_MODEL || "gpt-4o-mini";
1791
+ this.spinner.updateText(`🎨 Generating brand system with ${await this.ai.getActiveProviderName()} (${model})...`);
1778
1792
  const { text, provider } = await this.ai.generateText(prompt, {
1779
- model: options.model || process.env.MYCONTEXT_MODEL,
1793
+ model,
1780
1794
  modelCandidates: this.getModelCandidates(options),
1781
1795
  spinnerCallback: (text, resetTimer = false) => {
1782
1796
  this.spinner.updateText(text);
@@ -1787,6 +1801,13 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
1787
1801
  });
1788
1802
  // Parse and create the brand system files
1789
1803
  const brandFiles = this.parseAndCreateBrandSystem(text);
1804
+ // Update SSOT (Brain)
1805
+ await this.updateLivingContext({
1806
+ metadata: {
1807
+ lastUpdatedAt: new Date().toISOString(),
1808
+ status: "brand-generated"
1809
+ }
1810
+ });
1790
1811
  return {
1791
1812
  success: true,
1792
1813
  content: brandFiles.guide,
@@ -1891,23 +1912,37 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
1891
1912
  }
1892
1913
  }
1893
1914
  async generateComponentList(projectContext, options) {
1915
+ // 1. Try to load from Living Brain first
1916
+ const livingContext = await this.loadLivingContext();
1917
+ if (livingContext && livingContext.components && livingContext.components.length > 0 && !options.force) {
1918
+ this.spinner.updateText("📋 Using component architecture from Living Brain...");
1919
+ return {
1920
+ success: true,
1921
+ content: JSON.stringify(livingContext.components, null, 2),
1922
+ provider: "local",
1923
+ metadata: {
1924
+ model: "static",
1925
+ tokens: 0,
1926
+ latency: 0,
1927
+ },
1928
+ };
1929
+ }
1930
+ // 2. Fallback to AI generation if brain is empty or force is used
1931
+ this.spinner.updateText("📋 Discovering component architecture via AI...");
1894
1932
  // Load comprehensive context for better component generation
1895
1933
  const allContext = await this.loadAllContextFiles();
1896
1934
  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
1935
  const coreExcerpt = await this.readCoreExcerpt();
1936
+ // DEFAULT to gpt-4o-mini for planning tasks to avoid 8k token limits
1937
+ const model = options.model || process.env.MYCONTEXT_MODEL || "gpt-4o-mini";
1900
1938
  const prompt = [
1901
- `[mycontext] Plan: plan generate QA docs preview (→ checks)`,
1902
- `Create a detailed, business-specific component list for: ${projectContext.description || "MyContext project"}`,
1939
+ `[mycontext] Planning: Identify and decompose components for: ${projectContext.description || "MyContext project"}`,
1903
1940
  "",
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.",
1905
- "",
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.",
1941
+ "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
1942
  "",
1908
1943
  ...(coreExcerpt
1909
1944
  ? [
1910
- "Core component excerpt (use for visual/style consistency):",
1945
+ "Core component style (use for reference):",
1911
1946
  coreExcerpt,
1912
1947
  "",
1913
1948
  ]
@@ -1915,90 +1950,38 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
1915
1950
  "Business Context:",
1916
1951
  allContext.hasContext
1917
1952
  ? [
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
- : "",
1953
+ allContext.prd ? `## PRD\n${this.truncateContext(allContext.prd)}` : "",
1954
+ allContext.features ? `## Features\n${this.truncateContext(allContext.features)}` : "",
1955
+ allContext.userFlows ? `## User Flows\n${this.truncateContext(allContext.userFlows)}` : "",
1956
+ allContext.technicalSpecs ? `## Technical Specs\n${this.truncateContext(allContext.technicalSpecs)}` : "",
1929
1957
  ]
1930
1958
  .filter(Boolean)
1931
1959
  .join("\n\n")
1932
- : ctx.prd.split("\n").slice(0, 60).join("\n"),
1960
+ : this.truncateContext(ctx.prd, 2000),
1933
1961
  "",
1934
- "Types excerpt:",
1935
- ctx.types.split("\n").slice(0, 80).join("\n"),
1962
+ "Types Summary:",
1963
+ this.truncateContext(ctx.types, 3000),
1936
1964
  "",
1937
- "Branding excerpt:",
1938
- ctx.brand.split("\n").slice(0, 40).join("\n"),
1965
+ "Branding Summary:",
1966
+ this.truncateContext(ctx.brand, 1500),
1939
1967
  "",
1940
- "Available shadcn/ui primitives (import from '@/components/ui/<component>'):",
1968
+ "Available shadcn/ui primitives:",
1941
1969
  (ctx.shadcn.length ? ctx.shadcn : this.getCanonicalShadcnList())
1942
1970
  .slice(0, 60)
1943
1971
  .join(", "),
1944
1972
  "",
1945
- "Return strictly valid JSON ONLY with this HIERARCHICAL structure:",
1973
+ "Return strictly valid JSON with this structure:",
1946
1974
  "{",
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
- " }",
1975
+ ' "components": [',
1976
+ ' { "name": "ComponentName", "description": "Specific purpose", "type": "form|layout|display|interactive", "group": "GroupName", "status": "planned" }',
1977
+ " ],",
1978
+ ' "metadata": { "totalComponents": 0 }',
1983
1979
  "}",
1984
1980
  "",
1985
1981
  "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.",
1982
+ "- Logical React composition starting from root",
1983
+ "- Extract specific feature terminology from context",
1984
+ "- Return ONLY JSON - no markdown fences.",
2002
1985
  ].join("\n");
2003
1986
  try {
2004
1987
  // Check if user has local AI keys configured
@@ -2007,10 +1990,9 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
2007
1990
  let provider;
2008
1991
  if (hasLocalKeys) {
2009
1992
  // Use local AI first (user's own keys)
2010
- this.spinner.updateText(`📋 Generating component list with ${await this.ai.getActiveProviderName()}...`);
2011
1993
  try {
2012
1994
  const r = await this.ai.generateText(prompt, {
2013
- model: options.model || process.env.MYCONTEXT_MODEL,
1995
+ model,
2014
1996
  modelCandidates: this.getModelCandidates(options),
2015
1997
  spinnerCallback: (text, resetTimer = false) => {
2016
1998
  this.spinner.updateText(text);
@@ -2023,12 +2005,12 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
2023
2005
  provider = r.provider;
2024
2006
  }
2025
2007
  catch (e) {
2026
- // Handle transient network issues like unexpected EOF with a single retry
2008
+ // Retry logic (existing)
2027
2009
  const msg = String(e?.message || e);
2028
2010
  if (/unexpected EOF|ECONNRESET|EPIPE/i.test(msg)) {
2029
- this.spinner.updateText("Retrying component list generation after transient error...");
2011
+ this.spinner.updateText("Retrying component list generation...");
2030
2012
  const r2 = await this.ai.generateText(prompt, {
2031
- model: options.model || process.env.MYCONTEXT_MODEL,
2013
+ model,
2032
2014
  modelCandidates: this.getModelCandidates(options),
2033
2015
  spinnerCallback: (text, resetTimer = false) => {
2034
2016
  this.spinner.updateText(text);
@@ -2047,7 +2029,6 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
2047
2029
  }
2048
2030
  else {
2049
2031
  // No local keys - use hosted API (requires authentication)
2050
- this.spinner.updateText("📋 Generating component list with MyContext AI (hosted)...");
2051
2032
  const hostedResult = await this.hostedApi.generateContext("components-list", prompt, {
2052
2033
  model: options.model || "mycontext",
2053
2034
  context: projectContext,
@@ -2063,6 +2044,21 @@ Make the CSS immediately usable - no placeholders, actual working values!`;
2063
2044
  // Attempt to repair and extract valid JSON
2064
2045
  const repaired = this.repairJson(text);
2065
2046
  const cleanedContent = this.extractJson(repaired);
2047
+ // Update SSOT (Brain)
2048
+ try {
2049
+ const obj = JSON.parse(cleanedContent);
2050
+ await this.updateLivingContext({
2051
+ metadata: {
2052
+ lastUpdatedAt: new Date().toISOString(),
2053
+ status: "components-planned"
2054
+ }
2055
+ // Note: Full component hierarchy is heavy, keep in 04-component-list.json for now
2056
+ // or we could flatten and store in context.json.components
2057
+ });
2058
+ }
2059
+ catch (e) {
2060
+ // ignore parse errors for SSOT update; saveGeneratedContent will handle it later
2061
+ }
2066
2062
  // Attach rich context so JSON alone is enough for component generation
2067
2063
  try {
2068
2064
  console.log(`[GenerateCommand] Attempting to parse cleaned content (first 200 chars): ${cleanedContent.slice(0, 200)}...`);
@@ -3529,35 +3525,128 @@ export function Typography({ variant, children, className }: TypographyProps) {
3529
3525
  // 5) Fallback: wrap into minimal valid envelope
3530
3526
  return JSON.stringify({ error: "Invalid JSON from AI", raw: raw.slice(0, 500) }, null, 2);
3531
3527
  }
3528
+ async loadLivingContext() {
3529
+ try {
3530
+ const contextPath = path_1.default.join(this.getProjectRoot(), ".mycontext", "context.json");
3531
+ if (await fs.pathExists(contextPath)) {
3532
+ return await fs.readJson(contextPath);
3533
+ }
3534
+ }
3535
+ catch (e) {
3536
+ // ignore
3537
+ }
3538
+ return null;
3539
+ }
3540
+ async updateLivingContext(update) {
3541
+ try {
3542
+ const projectRoot = this.getProjectRoot();
3543
+ const contextPath = path_1.default.join(projectRoot, ".mycontext", "context.json");
3544
+ let current = await this.loadLivingContext();
3545
+ if (!current) {
3546
+ // Fallback to empty context if it doesn't exist
3547
+ current = {
3548
+ metadata: {
3549
+ version: "1.0.0",
3550
+ generatedAt: new Date().toISOString(),
3551
+ lastUpdatedAt: new Date().toISOString(),
3552
+ projectConfig: {
3553
+ id: require("crypto").randomUUID(),
3554
+ name: "MyContext Project",
3555
+ description: "",
3556
+ createdAt: new Date().toISOString(),
3557
+ updatedAt: new Date().toISOString(),
3558
+ contextPath: ".mycontext",
3559
+ version: "0.1.0",
3560
+ status: "initialized"
3561
+ }
3562
+ },
3563
+ prd: { title: "", problemStatement: "", goals: [], targetAudience: "", successMetrics: [] },
3564
+ features: [],
3565
+ flows: [],
3566
+ edgeCases: [],
3567
+ specs: {
3568
+ architecture: "",
3569
+ techStack: { frontend: [], backend: [], database: [], other: [] },
3570
+ apiEndpoints: [],
3571
+ databaseSchema: { tables: [] }
3572
+ },
3573
+ components: [],
3574
+ actions: [],
3575
+ routes: [],
3576
+ brain: { memory: {}, logs: [] }
3577
+ };
3578
+ }
3579
+ const { deepMerge } = await Promise.resolve().then(() => __importStar(require("../utils/deepMerge")));
3580
+ const merged = deepMerge(current, update);
3581
+ merged.metadata.lastUpdatedAt = new Date().toISOString();
3582
+ await fs.writeJson(contextPath, merged, { spaces: 2 });
3583
+ }
3584
+ catch (e) {
3585
+ console.log(chalk_1.default.yellow(`⚠️ Failed to update context.json: ${e instanceof Error ? e.message : String(e)}`));
3586
+ }
3587
+ }
3532
3588
  async readContextArtifacts() {
3533
3589
  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
3590
+ const cwd = this.getProjectRoot();
3591
+ const livingBrain = await this.loadLivingContext();
3539
3592
  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) {
3593
+ let types = "";
3594
+ let brand = "";
3595
+ if (livingBrain) {
3596
+ // Build PRD string from JSON
3547
3597
  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("");
3598
+ `# ${livingBrain.prd.title}`,
3599
+ `## Problem Statement\n${livingBrain.prd.problemStatement}`,
3600
+ `## Goals\n${livingBrain.prd.goals.map(g => `- ${g}`).join("\n")}`,
3601
+ livingBrain.features.length ? `## Features\n${livingBrain.features.map(f => `### ${f.name} (${f.priority})\n${f.description}`).join("\n\n")}` : "",
3602
+ livingBrain.flows.length ? `## Flows\n${livingBrain.flows.map(f => `### ${f.name}\n${f.steps.join("\n")}`).join("\n\n")}` : ""
3603
+ ].filter(Boolean).join("\n\n");
3554
3604
  }
3605
+ const readOrEmpty = async (rel) => (await fs.pathExists(path_1.default.join(cwd, rel)))
3606
+ ? await fs.readFile(path_1.default.join(cwd, rel), "utf8")
3607
+ : "";
3608
+ // Fallback/Augment PRD from files if JSON is sparse
3555
3609
  if (!prd) {
3556
- prd = await readOrEmpty(".mycontext/01-prd.md");
3610
+ const brief = (await readOrEmpty(".mycontext/01a-brief.md")) || (await readOrEmpty(".mycontext/01-brief.md"));
3611
+ const reqs = (await readOrEmpty(".mycontext/01b-requirements.md")) || (await readOrEmpty(".mycontext/02-requirements.md"));
3612
+ const flows = (await readOrEmpty(".mycontext/01c-flows.md")) || (await readOrEmpty(".mycontext/03-flows.md"));
3613
+ if (brief || reqs || flows) {
3614
+ prd = [
3615
+ brief ? "## Brief\n\n" + brief : "",
3616
+ reqs ? "\n\n## Requirements\n\n" + reqs : "",
3617
+ flows ? "\n\n## Flows\n\n" + flows : "",
3618
+ ].filter(Boolean).join("");
3619
+ }
3620
+ if (!prd) {
3621
+ prd = await readOrEmpty(".mycontext/01-prd.md");
3622
+ }
3623
+ }
3624
+ // Discover Types (Modern Multi-file)
3625
+ const typesDir = path_1.default.join(cwd, ".mycontext", "types");
3626
+ if (await fs.pathExists(typesDir)) {
3627
+ const files = await fs.readdir(typesDir);
3628
+ const typeFiles = files.filter(f => f.endsWith(".ts"));
3629
+ for (const file of typeFiles) {
3630
+ const content = await fs.readFile(path_1.default.join(typesDir, file), "utf8");
3631
+ types += `\n// --- ${file} ---\n${content}\n`;
3632
+ }
3633
+ }
3634
+ if (!types) {
3635
+ types = (await readOrEmpty(".mycontext/02-types.ts")) || (await readOrEmpty(".mycontext/02-types-guide.md"));
3636
+ }
3637
+ // Discover Brand (Modern Multi-file)
3638
+ const brandDir = path_1.default.join(cwd, ".mycontext", "brand");
3639
+ if (await fs.pathExists(brandDir)) {
3640
+ brand = await readOrEmpty(".mycontext/brand/globals.css");
3641
+ const brandFiles = await fs.readdir(brandDir);
3642
+ for (const file of brandFiles.filter(f => f.endsWith(".md"))) {
3643
+ const content = await fs.readFile(path_1.default.join(brandDir, file), "utf8");
3644
+ brand += `\n\n### ${file}\n${content}`;
3645
+ }
3646
+ }
3647
+ if (!brand) {
3648
+ brand = (await readOrEmpty(".mycontext/03-branding.md")) || (await readOrEmpty(".mycontext/03-branding-guide.md"));
3557
3649
  }
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
3650
  // discover shadcn/ui primitives from components/ui
3562
3651
  const uiDir = path_1.default.join(cwd, "components", "ui");
3563
3652
  let shadcn = [];
@@ -3836,35 +3925,7 @@ ${isEcommerce
3836
3925
  return aliases[type] || type;
3837
3926
  }
3838
3927
  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);
3928
+ return AICore_1.AICore.getInstance().hasAnyProvider();
3868
3929
  }
3869
3930
  getModelCandidates(options) {
3870
3931
  const raw = options.modelCandidates ||
@@ -3956,7 +4017,8 @@ INSTRUCTIONS:
3956
4017
  1. Analyze the project details provided above (which may include file structure, tech stack, and README summary).
3957
4018
  2. Extract functional requirements, user flows, and technical limits.
3958
4019
  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.
4020
+ 4. Identify all necessary UI components and group them logically (e.g. Auth, Sidebar, Dashboard).
4021
+ 5. Generate a complete PRD, Technical Spec, and Component Architecture that reflects this ACTUAL existing project.
3960
4022
  `;
3961
4023
  }
3962
4024
  // The schema for LivingContext (simplified version for the LLM)
@@ -3966,12 +4028,31 @@ INSTRUCTIONS:
3966
4028
  "features": [{ "id": "...", "name": "...", "description": "...", "priority": "high|medium|low", "userValue": "...", "acceptanceCriteria": [], "dependencies": [] }],
3967
4029
  "flows": [{ "id": "...", "name": "...", "description": "...", "steps": [], "actors": [] }],
3968
4030
  "edgeCases": [{ "id": "...", "category": "...", "description": "...", "mitigation": "..." }],
4031
+ "brand": {
4032
+ "theme": "light|dark",
4033
+ "colors": { "primary": "#...", "background": "#...", "text": "#...", "accent": "#..." },
4034
+ "typography": { "fontFamily": "...", "headingScale": "..." },
4035
+ "designPrinciples": []
4036
+ },
4037
+ "types": {
4038
+ "entities": [{ "name": "...", "description": "...", "schema": "export interface ... { ... }" }],
4039
+ "shared": [{ "name": "...", "description": "...", "schema": "export interface ... { ... }" }]
4040
+ },
3969
4041
  "specs": {
3970
4042
  "architecture": "...",
3971
4043
  "techStack": { "frontend": [], "backend": [], "database": [], "other": [] },
3972
4044
  "apiEndpoints": [{ "path": "...", "method": "...", "description": "...", "authRequired": true }],
3973
4045
  "databaseSchema": { "tables": [{ "name": "...", "columns": [{ "name": "...", "type": "...", "constraints": [] }] }] }
3974
- }
4046
+ },
4047
+ "components": [
4048
+ {
4049
+ "name": "ComponentName",
4050
+ "description": "...",
4051
+ "type": "form|layout|display|interactive",
4052
+ "group": "Auth|Dashboard|etc",
4053
+ "status": "planned"
4054
+ }
4055
+ ]
3975
4056
  }
3976
4057
  `;
3977
4058
  const livingContextData = await aiCore.generateStructuredText(prompt, schema);
@@ -4019,6 +4100,9 @@ INSTRUCTIONS:
4019
4100
  { name: "01b-user-flows.md", content: contextRenderer_1.ContextRenderer.renderUserFlows(livingContext) },
4020
4101
  { name: "01c-edge-cases.md", content: contextRenderer_1.ContextRenderer.renderEdgeCases(livingContext) },
4021
4102
  { name: "01d-technical-specs.md", content: contextRenderer_1.ContextRenderer.renderTechnicalSpecs(livingContext) },
4103
+ { name: "02-types-guide.md", content: contextRenderer_1.ContextRenderer.renderTypesGuide(livingContext) },
4104
+ { name: "03-brand-guide.md", content: contextRenderer_1.ContextRenderer.renderBrandGuide(livingContext) },
4105
+ { name: "04-component-list.md", content: contextRenderer_1.ContextRenderer.renderComponentList(livingContext) },
4022
4106
  ];
4023
4107
  for (const render of renders) {
4024
4108
  const filePath = path_1.default.join(mycontextDir, render.name);