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.
- package/README.md +39 -38
- package/dist/cli.js +15 -44
- package/dist/cli.js.map +1 -1
- package/dist/commands/generate-components.d.ts.map +1 -1
- package/dist/commands/generate-components.js +21 -18
- package/dist/commands/generate-components.js.map +1 -1
- package/dist/commands/generate.d.ts +3 -0
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +242 -158
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init-interactive.d.ts.map +1 -1
- package/dist/commands/init-interactive.js +30 -2
- package/dist/commands/init-interactive.js.map +1 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +194 -38
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/setup-complete.d.ts.map +1 -1
- package/dist/commands/setup-complete.js +4 -6
- package/dist/commands/setup-complete.js.map +1 -1
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +10 -58
- package/dist/commands/workflow.js.map +1 -1
- package/dist/core/ai/AICore.d.ts +4 -0
- package/dist/core/ai/AICore.d.ts.map +1 -1
- package/dist/core/ai/AICore.js +6 -0
- package/dist/core/ai/AICore.js.map +1 -1
- package/dist/tui/DashboardMode.d.ts +14 -0
- package/dist/tui/DashboardMode.d.ts.map +1 -0
- package/dist/tui/DashboardMode.js +75 -0
- package/dist/tui/DashboardMode.js.map +1 -0
- package/dist/tui/TUIClient.d.ts +9 -0
- package/dist/tui/TUIClient.d.ts.map +1 -1
- package/dist/tui/TUIClient.js +63 -0
- package/dist/tui/TUIClient.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/living-context.d.ts +1 -0
- package/dist/types/living-context.d.ts.map +1 -1
- package/dist/types/tui.d.ts +3 -1
- package/dist/types/tui.d.ts.map +1 -1
- package/dist/utils/contextEnricher.d.ts +2 -1
- package/dist/utils/contextEnricher.d.ts.map +1 -1
- package/dist/utils/contextEnricher.js +138 -1
- package/dist/utils/contextEnricher.js.map +1 -1
- package/dist/utils/contextRenderer.d.ts +3 -0
- package/dist/utils/contextRenderer.d.ts.map +1 -1
- package/dist/utils/contextRenderer.js +65 -0
- package/dist/utils/contextRenderer.js.map +1 -1
- package/dist/utils/fileSystem.d.ts.map +1 -1
- package/dist/utils/fileSystem.js +32 -1
- package/dist/utils/fileSystem.js.map +1 -1
- package/dist/utils/geminiClient.d.ts.map +1 -1
- package/dist/utils/geminiClient.js +10 -5
- package/dist/utils/geminiClient.js.map +1 -1
- package/dist/utils/githubModelsClient.d.ts.map +1 -1
- package/dist/utils/githubModelsClient.js +4 -1
- package/dist/utils/githubModelsClient.js.map +1 -1
- package/dist/utils/openRouterClient.d.ts.map +1 -1
- package/dist/utils/openRouterClient.js +1 -0
- package/dist/utils/openRouterClient.js.map +1 -1
- package/dist/utils/unifiedDesignContextLoader.d.ts.map +1 -1
- package/dist/utils/unifiedDesignContextLoader.js +14 -0
- package/dist/utils/unifiedDesignContextLoader.js.map +1 -1
- package/package.json +4 -2
- package/dist/commands/assemble-features.d.ts +0 -40
- package/dist/commands/assemble-features.d.ts.map +0 -1
- package/dist/commands/assemble-features.js +0 -383
- package/dist/commands/assemble-features.js.map +0 -1
- package/dist/commands/compile-prd.d.ts +0 -18
- package/dist/commands/compile-prd.d.ts.map +0 -1
- package/dist/commands/compile-prd.js +0 -253
- package/dist/commands/compile-prd.js.map +0 -1
- package/dist/commands/generate-context-files.d.ts +0 -44
- package/dist/commands/generate-context-files.d.ts.map +0 -1
- package/dist/commands/generate-context-files.js +0 -871
- 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:
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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]
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
1960
|
+
: this.truncateContext(ctx.prd, 2000),
|
|
1933
1961
|
"",
|
|
1934
|
-
"Types
|
|
1935
|
-
ctx.types
|
|
1962
|
+
"Types Summary:",
|
|
1963
|
+
this.truncateContext(ctx.types, 3000),
|
|
1936
1964
|
"",
|
|
1937
|
-
"Branding
|
|
1938
|
-
ctx.brand
|
|
1965
|
+
"Branding Summary:",
|
|
1966
|
+
this.truncateContext(ctx.brand, 1500),
|
|
1939
1967
|
"",
|
|
1940
|
-
"Available shadcn/ui primitives
|
|
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
|
|
1973
|
+
"Return strictly valid JSON with this structure:",
|
|
1946
1974
|
"{",
|
|
1947
|
-
' "
|
|
1948
|
-
' "description": "
|
|
1949
|
-
|
|
1950
|
-
'
|
|
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
|
-
"-
|
|
1987
|
-
"-
|
|
1988
|
-
"-
|
|
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
|
|
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
|
-
//
|
|
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
|
|
2011
|
+
this.spinner.updateText("Retrying component list generation...");
|
|
2030
2012
|
const r2 = await this.ai.generateText(prompt, {
|
|
2031
|
-
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 =
|
|
3535
|
-
const
|
|
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
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
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
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
.
|
|
3553
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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);
|