mycontext-cli 0.4.7 → 0.4.9

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 (73) hide show
  1. package/README.md +261 -67
  2. package/dist/agents/communication/AgentCommunicationManager.d.ts +27 -0
  3. package/dist/agents/communication/AgentCommunicationManager.d.ts.map +1 -0
  4. package/dist/agents/communication/AgentCommunicationManager.js +293 -0
  5. package/dist/agents/communication/AgentCommunicationManager.js.map +1 -0
  6. package/dist/agents/evolution/CodeEvolutionEngine.d.ts +92 -0
  7. package/dist/agents/evolution/CodeEvolutionEngine.d.ts.map +1 -0
  8. package/dist/agents/evolution/CodeEvolutionEngine.js +639 -0
  9. package/dist/agents/evolution/CodeEvolutionEngine.js.map +1 -0
  10. package/dist/agents/implementations/ArchitectAgent.d.ts +39 -0
  11. package/dist/agents/implementations/ArchitectAgent.d.ts.map +1 -0
  12. package/dist/agents/implementations/ArchitectAgent.js +345 -0
  13. package/dist/agents/implementations/ArchitectAgent.js.map +1 -0
  14. package/dist/agents/implementations/CodeGenSubAgent.d.ts +12 -0
  15. package/dist/agents/implementations/CodeGenSubAgent.d.ts.map +1 -1
  16. package/dist/agents/implementations/CodeGenSubAgent.js +303 -58
  17. package/dist/agents/implementations/CodeGenSubAgent.js.map +1 -1
  18. package/dist/agents/implementations/PromptConstructorAgent.d.ts +50 -0
  19. package/dist/agents/implementations/PromptConstructorAgent.d.ts.map +1 -0
  20. package/dist/agents/implementations/PromptConstructorAgent.js +481 -0
  21. package/dist/agents/implementations/PromptConstructorAgent.js.map +1 -0
  22. package/dist/agents/implementations/SecurityAgent.d.ts +31 -0
  23. package/dist/agents/implementations/SecurityAgent.d.ts.map +1 -0
  24. package/dist/agents/implementations/SecurityAgent.js +453 -0
  25. package/dist/agents/implementations/SecurityAgent.js.map +1 -0
  26. package/dist/agents/intelligence/ProjectIntelligence.d.ts +127 -0
  27. package/dist/agents/intelligence/ProjectIntelligence.d.ts.map +1 -0
  28. package/dist/agents/intelligence/ProjectIntelligence.js +456 -0
  29. package/dist/agents/intelligence/ProjectIntelligence.js.map +1 -0
  30. package/dist/agents/interfaces/AgentCommunication.d.ts +65 -0
  31. package/dist/agents/interfaces/AgentCommunication.d.ts.map +1 -0
  32. package/dist/agents/interfaces/AgentCommunication.js +13 -0
  33. package/dist/agents/interfaces/AgentCommunication.js.map +1 -0
  34. package/dist/agents/learning/CrossProjectLearning.d.ts +99 -0
  35. package/dist/agents/learning/CrossProjectLearning.d.ts.map +1 -0
  36. package/dist/agents/learning/CrossProjectLearning.js +517 -0
  37. package/dist/agents/learning/CrossProjectLearning.js.map +1 -0
  38. package/dist/cli.js +49 -0
  39. package/dist/cli.js.map +1 -1
  40. package/dist/commands/agent-flow.d.ts +21 -0
  41. package/dist/commands/agent-flow.d.ts.map +1 -0
  42. package/dist/commands/agent-flow.js +225 -0
  43. package/dist/commands/agent-flow.js.map +1 -0
  44. package/dist/commands/generate-components.d.ts +0 -13
  45. package/dist/commands/generate-components.d.ts.map +1 -1
  46. package/dist/commands/generate-components.js +42 -422
  47. package/dist/commands/generate-components.js.map +1 -1
  48. package/dist/commands/generate.d.ts +5 -1
  49. package/dist/commands/generate.d.ts.map +1 -1
  50. package/dist/commands/generate.js +381 -23
  51. package/dist/commands/generate.js.map +1 -1
  52. package/dist/commands/init.d.ts.map +1 -1
  53. package/dist/commands/init.js +12 -6
  54. package/dist/commands/init.js.map +1 -1
  55. package/dist/commands/predict.d.ts +36 -0
  56. package/dist/commands/predict.d.ts.map +1 -0
  57. package/dist/commands/predict.js +539 -0
  58. package/dist/commands/predict.js.map +1 -0
  59. package/dist/utils/clean.d.ts +6 -0
  60. package/dist/utils/clean.d.ts.map +1 -0
  61. package/dist/utils/clean.js +220 -0
  62. package/dist/utils/clean.js.map +1 -0
  63. package/dist/utils/githubModelsClient.d.ts.map +1 -1
  64. package/dist/utils/githubModelsClient.js +33 -16
  65. package/dist/utils/githubModelsClient.js.map +1 -1
  66. package/dist/utils/hybridAIClient.d.ts +1 -0
  67. package/dist/utils/hybridAIClient.d.ts.map +1 -1
  68. package/dist/utils/hybridAIClient.js +23 -9
  69. package/dist/utils/hybridAIClient.js.map +1 -1
  70. package/dist/utils/ollamaClient.d.ts.map +1 -1
  71. package/dist/utils/ollamaClient.js +6 -4
  72. package/dist/utils/ollamaClient.js.map +1 -1
  73. package/package.json +2 -1
@@ -88,6 +88,9 @@ class GenerateCommand {
88
88
  case "components-list":
89
89
  result = await this.generateComponentList(projectContext, options);
90
90
  break;
91
+ case "project-structure":
92
+ result = await this.generateProjectStructure(projectContext, options);
93
+ break;
91
94
  case "all": {
92
95
  // Run sequentially with chaining: PRD -> Types(from PRD) -> (optional Brand) -> Components List(from Types + optional brand)
93
96
  // PRD
@@ -129,6 +132,20 @@ class GenerateCommand {
129
132
  throw new Error(compListRes.error || "Components list generation failed");
130
133
  const compListPath = await this.saveGeneratedContent("components-list", compListRes.content, options);
131
134
  console.log(chalk_1.default.green(`✅ Generated components-list saved to: ${compListPath}`));
135
+ // Project Structure (use all context artifacts)
136
+ const structureRes = await this.generateProjectStructure(projectContext, {
137
+ ...options,
138
+ contextFile: brandPath
139
+ ? `${prdPath},${typesPath},${brandPath},${compListPath}`
140
+ : `${prdPath},${typesPath},${compListPath}`,
141
+ });
142
+ if (structureRes.success && structureRes.content) {
143
+ const structurePath = await this.saveGeneratedContent("project-structure", structureRes.content, options);
144
+ console.log(chalk_1.default.green(`✅ Generated project structure saved to: ${structurePath}`));
145
+ }
146
+ else {
147
+ console.log(chalk_1.default.yellow(`⚠️ Project structure generation skipped or failed: ${structureRes.error || "unknown"}`));
148
+ }
132
149
  this.spinner.success({ text: "All artifacts generated" });
133
150
  return;
134
151
  }
@@ -145,19 +162,37 @@ class GenerateCommand {
145
162
  try {
146
163
  const coreStatePath = path_1.default.join(process.cwd(), ".mycontext", "core.json");
147
164
  const hasCoreState = await fs.pathExists(coreStatePath);
148
- const coreState = hasCoreState ? await fs.readJson(coreStatePath) : {};
165
+ const coreState = hasCoreState
166
+ ? await fs.readJson(coreStatePath)
167
+ : {};
149
168
  if (!coreState?.name) {
150
169
  const raw = await fs.readFile(outputPath, "utf8");
151
170
  const json = JSON.parse(raw);
152
171
  const groups = Array.isArray(json.groups) ? json.groups : [];
172
+ const metaCandidates = Array.isArray(json?.metadata?.coreCandidates)
173
+ ? json.metadata.coreCandidates
174
+ : [];
175
+ const candidateKeys = new Set(metaCandidates.map((c) => `${c.group}::${c.name}`));
153
176
  const flat = [];
154
177
  for (const g of groups)
155
178
  for (const c of g.components || [])
156
179
  flat.push({ name: c.name, group: g.name, tags: c.tags });
157
180
  if (flat.length) {
158
- const preferred = flat.filter((c) => /core|canvas|layout|board/i.test(c.name) || (Array.isArray(c.tags) && c.tags.some((t) => /core|canvas/i.test(String(t)))));
159
- const choices = (preferred.length ? preferred : flat).map((c) => ({
160
- title: `${c.group} / ${c.name}`,
181
+ // Preferred by metadata first; then heuristic-based; then rest
182
+ const byKey = new Map(flat.map((c) => [`${c.group}::${c.name}`, c]));
183
+ const metaList = metaCandidates
184
+ .map((c) => byKey.get(`${c.group}::${c.name}`))
185
+ .filter(Boolean);
186
+ const heuristic = flat.filter((c) => (/core|canvas|layout|board/i.test(c.name) ||
187
+ (Array.isArray(c.tags) &&
188
+ c.tags.some((t) => /core|canvas/i.test(String(t))))) &&
189
+ !candidateKeys.has(`${c.group}::${c.name}`));
190
+ const rest = flat.filter((c) => !candidateKeys.has(`${c.group}::${c.name}`) &&
191
+ !heuristic.includes(c));
192
+ const ordered = [...metaList, ...heuristic, ...rest];
193
+ const choices = ordered.map((c) => ({
194
+ title: `${c.group} / ${c.name}` +
195
+ (candidateKeys.has(`${c.group}::${c.name}`) ? " ⭐" : ""),
161
196
  value: `${c.group}::${c.name}`,
162
197
  }));
163
198
  const pickNow = await (0, prompts_1.default)({
@@ -170,14 +205,24 @@ class GenerateCommand {
170
205
  });
171
206
  if (pickNow?.do) {
172
207
  const ans = await (0, prompts_1.default)({
173
- type: "select",
208
+ type: "autocomplete",
174
209
  name: "pick",
175
210
  message: "Pick the core component (design anchor)",
176
211
  choices,
212
+ suggest: (input, choicesArg) => {
213
+ const q = (input || "").toLowerCase();
214
+ return Promise.resolve(choicesArg.filter((c) => String(c.title).toLowerCase().includes(q)));
215
+ },
177
216
  });
178
217
  if (ans?.pick) {
179
218
  const [g, n] = String(ans.pick).split("::");
180
- const state = { name: n, group: g || "core", ready: false, updatedAt: new Date().toISOString(), refinements: [] };
219
+ const state = {
220
+ name: n,
221
+ group: g || "core",
222
+ ready: false,
223
+ updatedAt: new Date().toISOString(),
224
+ refinements: [],
225
+ };
181
226
  await fs.ensureDir(path_1.default.dirname(coreStatePath));
182
227
  await fs.writeJson(coreStatePath, state, { spaces: 2 });
183
228
  console.log(chalk_1.default.green(`✅ Core set to ${n} (group: ${g}).`));
@@ -313,14 +358,87 @@ class GenerateCommand {
313
358
  return await this.getProjectContext();
314
359
  }
315
360
  async generateContext(projectContext, options) {
316
- const prompt = `[mycontext] Plan: plan generate QA docs → preview (→ checks)\nCreate a PRD for: ${projectContext.description || "MyContext project"}
317
-
318
- Assume default stack unless user specifies otherwise:
319
- - Frontend: Next.js (App Router) with Shadcn UI
320
- - Auth/DB/File storage: InstantDB (auth, data, file persistence)
321
- - Testing: Jest + React Testing Library
322
-
323
- Include: overview, key features, user stories, tech requirements (reflecting the default stack), acceptance criteria. Keep it concise and actionable.`;
361
+ // If user provided --prd-file, import it as-is and return without AI
362
+ try {
363
+ const prdFile = options.prdFile ||
364
+ options.prdfile ||
365
+ options["prd-file"]; // accept variants
366
+ if (prdFile) {
367
+ const abs = path_1.default.isAbsolute(prdFile)
368
+ ? prdFile
369
+ : path_1.default.join(process.cwd(), prdFile);
370
+ if (await fs.pathExists(abs)) {
371
+ const content = await fs.readFile(abs, "utf8");
372
+ return {
373
+ success: true,
374
+ content,
375
+ provider: "hybrid",
376
+ metadata: { imported: true },
377
+ };
378
+ }
379
+ }
380
+ }
381
+ catch { }
382
+ const prompt = [
383
+ `[mycontext] Plan: plan → generate → QA → docs → preview (→ checks)`,
384
+ `Create a production-grade PRD for: ${projectContext.description || "MyContext project"}`,
385
+ "",
386
+ "Assume default stack unless user specifies otherwise:",
387
+ "- Frontend: Next.js (App Router) with Shadcn UI",
388
+ "- Auth/DB/File storage: InstantDB (auth, data, file persistence)",
389
+ "- Accessibility: Radix UI principles (keyboard nav, ARIA, focus mgmt)",
390
+ "- Testing: Jest + React Testing Library",
391
+ "",
392
+ "Follow this step-by-step process first, then structure the PRD (avoid generic statements):",
393
+ "",
394
+ "Process:",
395
+ "1) Problem Statement: restate the project description from the user's perspective (who is impacted, what problem exists today, why now)",
396
+ "2) Desired Outcomes & Success Metrics: what users want to eliminate/improve/create; define measurable outcomes",
397
+ "3) Primary Users & Roles: identify roles and their responsibilities/permissions",
398
+ "4) Current State & Pain Points: briefly describe how it works today (or assumptions) and pain points",
399
+ "5) Improvement Opportunities: what to eliminate/automate/simplify; list key constraints/assumptions",
400
+ "6) Solution Approach & Scope: MVP vs later; boundaries and tradeoffs",
401
+ "7) User Journeys & Flows: derive end-to-end flows per role (happy path + alternatives + edge cases)",
402
+ "8) Detailed User Stories: action-oriented stories with context and goals",
403
+ "9) Acceptance Criteria: Given/When/Then per critical story",
404
+ "10) Entity Model & Relationships: core entities and associations",
405
+ "11) Information Architecture: pages/routes and the shadcn/ui primitives likely used",
406
+ "12) Technical Requirements: SSR/ISR, server actions, data fetching, security, telemetry",
407
+ "13) Non-Functional Requirements: performance budgets, accessibility, i18n, reliability",
408
+ "14) Risks & Open Questions: what could derail the plan and what needs decisions",
409
+ "",
410
+ "Then, produce the PRD with the following sections:",
411
+ "Structure the PRD with depth (avoid generic statements):",
412
+ "1) Overview: goal, non-goals, success metrics",
413
+ "2) User Roles & Responsibilities: distinct roles with responsibilities and permissions",
414
+ "3) Personas (optional): brief traits that influence UX",
415
+ "4) User Journeys & Flows: end-to-end scenarios per role (step-by-step)",
416
+ "5) Detailed User Stories: action-oriented; include negative flows and edge cases",
417
+ "6) Entity Model & Relationships: main entities and associations",
418
+ "7) Information Architecture: pages/routes, key UI patterns (shadcn components)",
419
+ "8) Technical Requirements: SSR/ISR, data fetching, server actions, security, telemetry",
420
+ "9) Non-Functional Requirements: performance budgets, accessibility, internationalization",
421
+ "10) Acceptance Criteria: Gherkin-style Given/When/Then per critical story",
422
+ "11) Risks & Open Questions",
423
+ "",
424
+ "Include two diagrams in fenced code blocks:",
425
+ "- Sequence diagram of a critical flow (e.g., auth → gameplay → result)",
426
+ "- (Optional) Flowchart for a user journey",
427
+ "",
428
+ "```mermaid",
429
+ "sequenceDiagram",
430
+ " autonumber",
431
+ " participant User",
432
+ " participant App as Next.js App",
433
+ " participant DB as InstantDB",
434
+ " User->>App: Perform primary action",
435
+ " App->>DB: Persist/fetch data",
436
+ " DB-->>App: Data OK",
437
+ " App-->>User: Updated UI",
438
+ "```",
439
+ "",
440
+ "Write concretely. Ground content in the provided description. No placeholders.",
441
+ ].join("\n");
324
442
  try {
325
443
  this.spinner.updateText(`Generating PRD with AI (${await this.ai.getActiveProviderName()}/${await this.ai.getActiveTextModelName()})...`);
326
444
  const { text, provider } = await this.ai.generateText(prompt, {
@@ -377,6 +495,7 @@ Include: data models, API interfaces, component props, utility types. Keep it co
377
495
  };
378
496
  }
379
497
  }
498
+ // specs flow removed; PRD is the requirements and Brand is the design.
380
499
  sanitizeTypeScriptOutput(raw) {
381
500
  // Prefer fenced code block content if present
382
501
  const block = raw.match(/```(?:ts|tsx|typescript|js)?\n([\s\S]*?)```/);
@@ -437,6 +556,56 @@ Include: identity, colors, typography, visual elements, brand voice. Keep it con
437
556
  };
438
557
  }
439
558
  }
559
+ async generateProjectStructure(projectContext, options) {
560
+ this.spinner.updateText("Generating project structure...");
561
+ try {
562
+ // Read existing context artifacts
563
+ const contextArtifacts = await this.readContextArtifacts();
564
+ const prd = contextArtifacts.prd;
565
+ const types = contextArtifacts.types;
566
+ // Read component list specifically
567
+ const compListPath = path_1.default.join(process.cwd(), ".mycontext", "04-component-list.json");
568
+ let compList = null;
569
+ if (await fs.pathExists(compListPath)) {
570
+ try {
571
+ const compListRaw = await fs.readFile(compListPath, "utf8");
572
+ compList = JSON.parse(compListRaw);
573
+ }
574
+ catch {
575
+ // Ignore parse errors
576
+ }
577
+ }
578
+ if (!compList?.groups?.length) {
579
+ throw new Error("Component list is required for project structure generation. Run: mycontext generate components-list");
580
+ }
581
+ const structurePrompt = this.buildProjectStructurePrompt(projectContext.description || "", prd, types, compList);
582
+ const candidates = this.getModelCandidates(options);
583
+ const result = await this.ai.generateText(structurePrompt, {
584
+ modelCandidates: candidates,
585
+ temperature: 0.3,
586
+ });
587
+ if (!result?.text) {
588
+ throw new Error("Failed to generate project structure");
589
+ }
590
+ const sanitized = this.sanitizeMarkdownOutput(result.text);
591
+ return {
592
+ success: true,
593
+ content: sanitized,
594
+ provider: result.provider ||
595
+ "hybrid",
596
+ };
597
+ }
598
+ catch (error) {
599
+ console.error(chalk_1.default.red(`Project structure generation failed: ${error.message}`));
600
+ // Fallback: Generate basic structure based on component list
601
+ const fallback = this.buildFallbackProjectStructure(projectContext);
602
+ return {
603
+ success: true,
604
+ content: fallback,
605
+ provider: "hybrid",
606
+ };
607
+ }
608
+ }
440
609
  async generateComponentList(projectContext, options) {
441
610
  const ctx = await this.readContextArtifacts();
442
611
  // Gate on core readiness if core.json exists
@@ -500,12 +669,21 @@ Include: identity, colors, typography, visual elements, brand voice. Keep it con
500
669
  " ]",
501
670
  " }",
502
671
  " ]",
672
+ " ,",
673
+ ' "metadata": {',
674
+ ' "coreCandidates": [',
675
+ ' { "name": string, "group": string, "reason": string },',
676
+ ' { "name": string, "group": string, "reason": string },',
677
+ ' { "name": string, "group": string, "reason": string }',
678
+ " ]",
679
+ " }",
503
680
  "}",
504
681
  "",
505
682
  "Rules:",
506
683
  "- No code fences, no comments, no trailing commas.",
507
684
  "- Use only the fields above.",
508
685
  "- Ensure arrays/objects have no extra commas.",
686
+ "- Provide up to 3 'coreCandidates' that best represent the design anchor (canvas/layout), with a short reason.",
509
687
  ].join("\n");
510
688
  try {
511
689
  this.spinner.updateText(`Generating component list with AI (${await this.ai.getActiveProviderName()}/${await this.ai.getActiveTextModelName()})...`);
@@ -650,7 +828,31 @@ Include: identity, colors, typography, visual elements, brand voice. Keep it con
650
828
  switch (type) {
651
829
  case "context": {
652
830
  outputPath = path_1.default.join(outputDir, "01-prd.md");
653
- await fs.writeFile(outputPath, content);
831
+ const preserve = Boolean(options.preservePrd);
832
+ if (preserve && (await fs.pathExists(outputPath))) {
833
+ // Preserve existing PRD; write a copy next to it for reference
834
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
835
+ const alt = path_1.default.join(outputDir, `01-prd.imported-${timestamp}.md`);
836
+ await fs.writeFile(alt, content);
837
+ }
838
+ else {
839
+ await fs.writeFile(outputPath, content);
840
+ }
841
+ // Also write split artifacts for improved DX (idempotent)
842
+ try {
843
+ const sections = this.splitPrdContent(content);
844
+ if (sections.brief.trim().length > 0) {
845
+ await fs.writeFile(path_1.default.join(outputDir, "01a-brief.md"), sections.brief);
846
+ }
847
+ if (sections.requirements.trim().length > 0) {
848
+ await fs.writeFile(path_1.default.join(outputDir, "01b-requirements.md"), sections.requirements);
849
+ }
850
+ if (sections.flows.trim().length > 0) {
851
+ await fs.writeFile(path_1.default.join(outputDir, "01c-flows.md"), sections.flows);
852
+ }
853
+ console.log(chalk_1.default.gray(" ↳ Also wrote split context: 01a-brief.md, 01b-requirements.md, 01c-flows.md"));
854
+ }
855
+ catch { }
654
856
  break;
655
857
  }
656
858
  case "types": {
@@ -690,6 +892,11 @@ Include: identity, colors, typography, visual elements, brand voice. Keep it con
690
892
  }
691
893
  break;
692
894
  }
895
+ case "project-structure": {
896
+ outputPath = path_1.default.join(outputDir, "05-project-structure.md");
897
+ await fs.writeFile(outputPath, content);
898
+ break;
899
+ }
693
900
  default: {
694
901
  // Fallback: keep timestamped file for unknown types
695
902
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
@@ -749,11 +956,40 @@ Include: identity, colors, typography, visual elements, brand voice. Keep it con
749
956
  return Array.from(out.values());
750
957
  }
751
958
  sanitizeMarkdownOutput(raw) {
752
- // Unwrap any fenced blocks to plain markdown text
753
- return raw
754
- .replace(/```[a-zA-Z]*\n?/g, "")
755
- .replace(/```\n?/g, "")
756
- .trim();
959
+ // Preserve fenced code blocks (e.g., ```mermaid, ```ts) while stripping stray outer fences only
960
+ // If the model wrapped the whole document in a single fence, unwrap once; otherwise keep inner fences.
961
+ const singleFence = raw.match(/^```[a-zA-Z]*\n[\s\S]*\n```\s*$/);
962
+ if (singleFence) {
963
+ // unwrap outer fence only
964
+ return raw
965
+ .replace(/^```[a-zA-Z]*\n/, "")
966
+ .replace(/\n```\s*$/, "")
967
+ .trim();
968
+ }
969
+ return raw.trim();
970
+ }
971
+ splitPrdContent(raw) {
972
+ try {
973
+ const text = String(raw);
974
+ // Heuristic: look for section anchors
975
+ const idxReq = text.search(/###\s+Requirements|###\s+Detailed User Stories|###\s+Acceptance Criteria/i);
976
+ const idxFlows = text.search(/###\s+Flows|###\s+User Journeys|```mermaid/i);
977
+ let brief = text;
978
+ let requirements = "";
979
+ let flows = "";
980
+ if (idxReq !== -1) {
981
+ brief = text.slice(0, idxReq).trim();
982
+ const secondCut = idxFlows !== -1 && idxFlows > idxReq ? idxFlows : text.length;
983
+ requirements = text.slice(idxReq, secondCut).trim();
984
+ if (idxFlows !== -1) {
985
+ flows = text.slice(idxFlows).trim();
986
+ }
987
+ }
988
+ return { brief, requirements, flows };
989
+ }
990
+ catch {
991
+ return { brief: raw, requirements: "", flows: "" };
992
+ }
757
993
  }
758
994
  buildSkeleton(type, projectContext) {
759
995
  switch (type) {
@@ -856,7 +1092,26 @@ Include: identity, colors, typography, visual elements, brand voice. Keep it con
856
1092
  const readOrEmpty = async (rel) => (await fs.pathExists(path_1.default.join(cwd, rel)))
857
1093
  ? await fs.readFile(path_1.default.join(cwd, rel), "utf8")
858
1094
  : "";
859
- const prd = await readOrEmpty(".mycontext/01-prd.md");
1095
+ // Prefer split files if present
1096
+ let prd = "";
1097
+ const brief = (await readOrEmpty(".mycontext/01a-brief.md")) ||
1098
+ (await readOrEmpty(".mycontext/01-brief.md"));
1099
+ const reqs = (await readOrEmpty(".mycontext/01b-requirements.md")) ||
1100
+ (await readOrEmpty(".mycontext/02-requirements.md"));
1101
+ const flows = (await readOrEmpty(".mycontext/01c-flows.md")) ||
1102
+ (await readOrEmpty(".mycontext/03-flows.md"));
1103
+ if (brief || reqs || flows) {
1104
+ prd = [
1105
+ brief ? "## Brief\n\n" + brief : "",
1106
+ reqs ? "\n\n## Requirements\n\n" + reqs : "",
1107
+ flows ? "\n\n## Flows\n\n" + flows : "",
1108
+ ]
1109
+ .filter(Boolean)
1110
+ .join("");
1111
+ }
1112
+ if (!prd) {
1113
+ prd = await readOrEmpty(".mycontext/01-prd.md");
1114
+ }
860
1115
  const types = await readOrEmpty(".mycontext/02-types.ts");
861
1116
  const brand = await readOrEmpty(".mycontext/03-branding.md");
862
1117
  // discover shadcn/ui primitives from components/ui
@@ -966,11 +1221,106 @@ Include: identity, colors, typography, visual elements, brand voice. Keep it con
966
1221
  repairJson(raw) {
967
1222
  // Remove any fenced code blocks markers
968
1223
  let s = raw.replace(/```json\n?|```/gi, "");
969
- // Remove stray " \" ", and trailing commas before ] or }
1224
+ // Remove stray tokens and trailing commas before ] or }
970
1225
  s = s.replace(/\s*" \" ",?\s*/g, "");
971
1226
  s = s.replace(/,\s*(\]|\})/g, "$1");
1227
+ // Quote bare identifiers for "name" fields, e.g., { "name": Dashboard } → { "name": "Dashboard" }
1228
+ // Handles simple PascalCase/word identifiers without quotes
1229
+ s = s.replace(/("name"\s*:\s*)([A-Za-z_][A-Za-z0-9_]*)/g, (_m, p1, p2) => `${p1}"${p2}"`);
972
1230
  return s;
973
1231
  }
1232
+ buildProjectStructurePrompt(description, prd, types, compList) {
1233
+ const groups = compList.groups || [];
1234
+ const componentSummary = groups
1235
+ .map((g) => `${g.name} (${g.components?.length || 0} components): ${g.description}`)
1236
+ .join("\n");
1237
+ return `Generate a Next.js project structure for: ${description}
1238
+
1239
+ DESIGN PRINCIPLES:
1240
+ - Prioritize dialogs for details/forms/interactions (reduce page count)
1241
+ - Use datatables for reports and data metrics
1242
+ - Minimize standalone pages - favor modal workflows
1243
+ - Follow Next.js App Router patterns
1244
+
1245
+ EXISTING CONTEXT:
1246
+ ## PRD Summary:
1247
+ ${prd.substring(0, 500)}...
1248
+
1249
+ ## Component Groups:
1250
+ ${componentSummary}
1251
+
1252
+ ## Available Types:
1253
+ ${types.substring(0, 300)}...
1254
+
1255
+ PROJECT STRUCTURE REQUIREMENTS:
1256
+ 1. Generate a realistic Next.js 13+ app directory structure
1257
+ 2. Create minimal pages - favor dialogs and datatables
1258
+ 3. Include API routes for each data entity
1259
+ 4. Structure for the specific app type and complexity
1260
+ 5. Show how components will be organized
1261
+ 6. Include middleware, layouts, and error boundaries
1262
+ 7. Plan for state management and data fetching
1263
+
1264
+ Format as a markdown file with:
1265
+ - Clear directory tree structure
1266
+ - Explanation of each major directory/file
1267
+ - Component placement strategy
1268
+ - API route organization
1269
+ - State management approach
1270
+
1271
+ Generate the complete project structure now:`;
1272
+ }
1273
+ buildFallbackProjectStructure(projectContext) {
1274
+ const appType = projectContext.description?.toLowerCase() || "general";
1275
+ const isEcommerce = appType.includes("ecommerce") || appType.includes("shop");
1276
+ const isDashboard = appType.includes("dashboard") || appType.includes("admin");
1277
+ return `# Project Structure
1278
+
1279
+ ## Core Next.js Structure
1280
+ \`\`\`
1281
+ ${projectContext.name || "project"}/
1282
+ ├── src/
1283
+ │ ├── app/
1284
+ │ │ ├── (auth)/
1285
+ │ │ │ ├── login/page.tsx
1286
+ │ │ │ └── register/page.tsx
1287
+ │ │ ├── dashboard/
1288
+ │ │ │ ├── page.tsx # Main dashboard with dialogs
1289
+ │ │ │ └── loading.tsx
1290
+ │ │ ├── api/
1291
+ │ │ │ ├── auth/route.ts
1292
+ ${isEcommerce
1293
+ ? "│ │ │ ├── products/route.ts\n│ │ │ ├── orders/route.ts\n│ │ │ └── cart/route.ts"
1294
+ : "│ │ │ ├── users/route.ts\n│ │ │ └── data/route.ts"}
1295
+ │ │ ├── globals.css
1296
+ │ │ ├── layout.tsx
1297
+ │ │ └── page.tsx # Landing page
1298
+ │ ├── components/
1299
+ │ │ ├── ui/ # shadcn/ui components
1300
+ │ │ ├── dialogs/ # Modal dialogs for forms/details
1301
+ │ │ │ ├── CreateDialog.tsx
1302
+ │ │ │ ├── EditDialog.tsx
1303
+ │ │ │ └── DetailsDialog.tsx
1304
+ │ │ ├── tables/ # Data tables for reports
1305
+ │ │ │ ├── DataTable.tsx
1306
+ │ │ │ └── ReportsTable.tsx
1307
+ │ │ └── forms/ # Reusable form components
1308
+ │ └── lib/
1309
+ │ ├── utils.ts
1310
+ │ ├── validations.ts
1311
+ │ └── api.ts
1312
+ ├── components.json
1313
+ └── package.json
1314
+ \`\`\`
1315
+
1316
+ ## Design Strategy
1317
+ - **Minimal Pages**: Use dialogs for forms and detail views
1318
+ - **Datatables**: Centralized data display with inline actions
1319
+ - **Modal-First**: Most interactions happen in dialogs
1320
+ - **API-Driven**: Each entity has dedicated API routes
1321
+ - **Component Organization**: Group by function, not feature
1322
+ `;
1323
+ }
974
1324
  printNextStepsAfterGenerate(type) {
975
1325
  try {
976
1326
  switch (type) {
@@ -983,12 +1333,20 @@ Include: identity, colors, typography, visual elements, brand voice. Keep it con
983
1333
  console.log(chalk_1.default.gray(" mycontext generate components-list # alias: component-list"));
984
1334
  break;
985
1335
  case "components-list":
986
- console.log(chalk_1.default.blue("\n➡️ Next: Generate components"));
1336
+ console.log(chalk_1.default.blue("\n➡️ Next: Generate project structure"));
1337
+ console.log(chalk_1.default.gray(" mycontext generate project-structure"));
1338
+ console.log(chalk_1.default.blue("\n💡 Then: Generate components"));
987
1339
  console.log(chalk_1.default.gray(" mycontext generate-components all --with-tests # optional tests"));
988
1340
  console.log(chalk_1.default.blue("➡️ Preview:"));
989
1341
  console.log(chalk_1.default.gray(" Visit /preview (dev server)"));
990
1342
  console.log(chalk_1.default.gray(" mycontext normalize preview # optional final layout"));
991
1343
  break;
1344
+ case "project-structure":
1345
+ console.log(chalk_1.default.blue("\n➡️ Next: Generate components"));
1346
+ console.log(chalk_1.default.gray(" mycontext generate-components all --with-tests # optional tests"));
1347
+ console.log(chalk_1.default.blue("➡️ Preview:"));
1348
+ console.log(chalk_1.default.gray(" Visit /preview (dev server)"));
1349
+ break;
992
1350
  default:
993
1351
  break;
994
1352
  }