@getcoherent/cli 0.6.14 → 0.6.16

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.
@@ -3,7 +3,7 @@ import {
3
3
  detectAIProvider,
4
4
  getAPIKey,
5
5
  hasAnyAPIKey
6
- } from "./chunk-25JRF5MA.js";
6
+ } from "./chunk-SPQZBQYY.js";
7
7
  import "./chunk-3RG5ZIWI.js";
8
8
  export {
9
9
  createAIProvider,
@@ -253,7 +253,7 @@ Style context: ${styleContext || "default"}
253
253
  ${designRules}
254
254
 
255
255
  Requirements:
256
- - Each component MUST have \`export default function ComponentName\`
256
+ - Each component MUST use a NAMED export: \`export function ComponentName\` (NOT export default)
257
257
  - Use shadcn/ui imports from @/components/ui/*
258
258
  - Use Tailwind CSS classes matching the style context
259
259
  - TypeScript with proper props interface
@@ -269,9 +269,10 @@ Return JSON with { requests: [{ type: "add-page", changes: { name: "ComponentNam
269
269
  (r) => r.type === "add-page" && r.changes?.name === comp.name
270
270
  );
271
271
  const code = match?.changes?.pageCode;
272
- if (code && code.includes("export default")) {
272
+ if (code && (code.includes("export function") || code.includes("export default"))) {
273
+ const fixedCode = code.replace(/export default function (\w+)/g, "export function $1");
273
274
  const file = `components/shared/${toKebabCase(comp.name)}.tsx`;
274
- results.push({ name: comp.name, code, file });
275
+ results.push({ name: comp.name, code: fixedCode, file });
275
276
  }
276
277
  }
277
278
  } catch {
@@ -284,9 +285,10 @@ Return JSON with { requests: [{ type: "add-page", changes: { name: "ComponentNam
284
285
  (r) => r.type === "add-page" && r.changes?.name === comp.name
285
286
  );
286
287
  const code = match?.changes?.pageCode;
287
- if (code && code.includes("export default")) {
288
+ if (code && (code.includes("export function") || code.includes("export default"))) {
289
+ const fixedCode = code.replace(/export default function (\w+)/g, "export function $1");
288
290
  const file = `components/shared/${toKebabCase(comp.name)}.tsx`;
289
- results.push({ name: comp.name, code, file });
291
+ results.push({ name: comp.name, code: fixedCode, file });
290
292
  }
291
293
  } catch {
292
294
  }
@@ -68,7 +68,7 @@ Please set ${envVar} in your environment or .env file.`);
68
68
  }
69
69
  if (preferredProvider === "openai") {
70
70
  try {
71
- const { OpenAIClient } = await import("./openai-provider-XUI7ZHUR.js");
71
+ const { OpenAIClient } = await import("./openai-provider-4KGARVC3.js");
72
72
  return await OpenAIClient.create(apiKey2, config?.model);
73
73
  } catch (error) {
74
74
  if (error.message?.includes("not installed")) {
@@ -82,7 +82,7 @@ Error: ${error.message}`
82
82
  );
83
83
  }
84
84
  } else {
85
- const { ClaudeClient } = await import("./claude-BZ3HSBD3.js");
85
+ const { ClaudeClient } = await import("./claude-K5B5ITAT.js");
86
86
  return ClaudeClient.create(apiKey2, config?.model);
87
87
  }
88
88
  }
@@ -95,7 +95,7 @@ Error: ${error.message}`
95
95
  switch (provider) {
96
96
  case "openai":
97
97
  try {
98
- const { OpenAIClient } = await import("./openai-provider-XUI7ZHUR.js");
98
+ const { OpenAIClient } = await import("./openai-provider-4KGARVC3.js");
99
99
  return await OpenAIClient.create(apiKey, config?.model);
100
100
  } catch (error) {
101
101
  if (error.message?.includes("not installed")) {
@@ -109,7 +109,7 @@ Error: ${error.message}`
109
109
  );
110
110
  }
111
111
  case "claude":
112
- const { ClaudeClient } = await import("./claude-BZ3HSBD3.js");
112
+ const { ClaudeClient } = await import("./claude-K5B5ITAT.js");
113
113
  return ClaudeClient.create(apiKey, config?.model);
114
114
  default:
115
115
  throw new Error(`Unsupported AI provider: ${provider}`);
@@ -399,7 +399,7 @@ Rules:
399
399
  - Do NOT use these names (already shared): ${existingSharedNames.join(", ")}
400
400
  - Look for: cards with icon+title+description, pricing tiers, testimonial blocks, stat displays, CTA sections
401
401
 
402
- Each component object: "name" (PascalCase), "type" ("section"|"widget"), "description", "propsInterface", "code" (full TSX module as string)
402
+ Each component object: "name" (PascalCase), "type" ("layout"|"navigation"|"data-display"|"form"|"feedback"|"section"|"widget"), "description", "propsInterface", "code" (full TSX module as string)
403
403
 
404
404
  If no repeating patterns found: { "components": [] }`
405
405
  }
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createAIProvider
3
- } from "./chunk-25JRF5MA.js";
3
+ } from "./chunk-SPQZBQYY.js";
4
4
  import {
5
5
  autoFixCode,
6
6
  checkDesignConsistency,
@@ -14,8 +14,9 @@ import {
14
14
  getPageType,
15
15
  loadPlan,
16
16
  routeToKey,
17
- savePlan
18
- } from "./chunk-WRDWFCQJ.js";
17
+ savePlan,
18
+ updateArchitecturePlan
19
+ } from "./chunk-PVJJ2YXP.js";
19
20
  import {
20
21
  CORE_CONSTRAINTS,
21
22
  DESIGN_QUALITY,
@@ -2464,7 +2465,7 @@ import { loadManifest } from "@getcoherent/core";
2464
2465
  import { DesignSystemManager } from "@getcoherent/core";
2465
2466
  function buildSharedComponentsListForClaude(manifest) {
2466
2467
  if (!manifest.shared || manifest.shared.length === 0) {
2467
- return "No shared components yet. Register with: coherent components shared add <name> --type layout|section|widget";
2468
+ return "No shared components yet. Register with: coherent components shared add <name> --type layout|navigation|data-display|form|feedback|section|widget";
2468
2469
  }
2469
2470
  const order = {
2470
2471
  layout: 0,
@@ -2959,7 +2960,7 @@ function buildSharedComponentsList(manifest) {
2959
2960
  if (!manifest.shared || manifest.shared.length === 0) {
2960
2961
  return `No shared components registered yet.
2961
2962
  When you create reusable blocks (headers, footers, repeated sections),
2962
- register them: coherent components shared add <Name> --type layout|section|widget`;
2963
+ register them: coherent components shared add <Name> --type layout|navigation|data-display|form|feedback|section|widget`;
2963
2964
  }
2964
2965
  const typeOrder = {
2965
2966
  layout: 0,
@@ -3857,7 +3858,7 @@ import {
3857
3858
  CLI_VERSION as CLI_VERSION2,
3858
3859
  getTemplateForPageType as getTemplateForPageType2,
3859
3860
  loadManifest as loadManifest8,
3860
- saveManifest as saveManifest2,
3861
+ saveManifest as saveManifest3,
3861
3862
  updateEntry
3862
3863
  } from "@getcoherent/core";
3863
3864
 
@@ -4101,7 +4102,7 @@ function detectPageType(pageName) {
4101
4102
  const normalized = pageName.toLowerCase();
4102
4103
  if (/dashboard|admin|overview/.test(normalized)) return "dashboard";
4103
4104
  if (/login|signin|sign-in/.test(normalized)) return "login";
4104
- if (/register|signup|sign.?up/.test(normalized)) return "register";
4105
+ if (/regist(?:er|ration)|signup|sign.?up/.test(normalized)) return "register";
4105
4106
  if (/pricing|plans|subscription/.test(normalized)) return "pricing";
4106
4107
  if (/about|company/.test(normalized)) return "about";
4107
4108
  if (/contact|support|help/.test(normalized)) return "contact";
@@ -4171,7 +4172,8 @@ async function parseModification(message, context, provider = "auto", options) {
4171
4172
  const prompt = buildModificationPrompt(enhancedMessage, context.config, componentRegistry, {
4172
4173
  isExpandedPageRequest,
4173
4174
  sharedComponentsSummary: options?.sharedComponentsSummary,
4174
- tieredComponentsPrompt: options?.tieredComponentsPrompt
4175
+ tieredComponentsPrompt: options?.tieredComponentsPrompt,
4176
+ reusePlanDirective: options?.reusePlanDirective
4175
4177
  });
4176
4178
  const raw = await ai.parseModification(prompt);
4177
4179
  const requestsArray = Array.isArray(raw) ? raw : raw?.requests ?? [];
@@ -4237,7 +4239,14 @@ Rules:
4237
4239
  function buildModificationPrompt(message, config2, componentRegistry, options) {
4238
4240
  const now = (/* @__PURE__ */ new Date()).toISOString();
4239
4241
  const expandedHint = options?.isExpandedPageRequest === true ? "\nIMPORTANT: The user request has been expanded with best practices. Use ALL the details provided when generating sections and content.\n\n" : "";
4240
- const sharedSection = options?.tieredComponentsPrompt ? `
4242
+ const sharedSection = options?.reusePlanDirective ? `
4243
+
4244
+ ## COMPONENT REUSE DIRECTIVE
4245
+
4246
+ ${options.reusePlanDirective}
4247
+
4248
+ For editing an existing shared component use type "modify-layout-block" with target "CID-XXX" or name.
4249
+ ` : options?.tieredComponentsPrompt ? `
4241
4250
 
4242
4251
  ## SHARED COMPONENTS (MANDATORY REUSE)
4243
4252
 
@@ -4520,9 +4529,9 @@ Return valid JSON only, no markdown code fence. Use this shape:
4520
4529
  { "requests": [ ... array of ModificationRequest ... ], "uxRecommendations": "optional markdown or omit key" }
4521
4530
  Legacy: returning only a JSON array of requests is still accepted.`;
4522
4531
  }
4523
- function buildLightweightPagePrompt(pageName, route, styleContext, sharedComponentsSummary, pageType, tieredComponentsPrompt) {
4532
+ function buildLightweightPagePrompt(pageName, route, styleContext, sharedComponentsSummary, pageType, tieredComponentsPrompt, reusePlanDirective) {
4524
4533
  const designConstraints = pageType ? getDesignQualityForType(pageType) : "";
4525
- const sharedNote = tieredComponentsPrompt || (sharedComponentsSummary ? `Available shared components:
4534
+ const sharedNote = reusePlanDirective || tieredComponentsPrompt || (sharedComponentsSummary ? `Available shared components:
4526
4535
  ${sharedComponentsSummary}` : "");
4527
4536
  return [
4528
4537
  `Generate complete pageCode for a page called "${pageName}" at route "${route}".`,
@@ -5028,7 +5037,7 @@ var AUTH_SYNONYMS = {
5028
5037
  };
5029
5038
  function deduplicatePages(pages) {
5030
5039
  const canonicalize = (route) => AUTH_SYNONYMS[route] || route;
5031
- const normalize = (route) => canonicalize(route).replace(/\/$/, "").replace(/s$/, "").replace(/ue$/, "");
5040
+ const normalize = (route) => canonicalize(route).replace(/\/$/, "");
5032
5041
  const seen = /* @__PURE__ */ new Map();
5033
5042
  return pages.filter((page, idx) => {
5034
5043
  const norm = normalize(page.route);
@@ -5050,10 +5059,10 @@ function extractComponentIdsFromCode(code) {
5050
5059
  return ids;
5051
5060
  }
5052
5061
  async function warnInlineDuplicates(projectRoot, pageName, route, pageCode, manifest, plan) {
5053
- const sectionOrWidget = manifest.shared.filter((e) => e.type === "section" || e.type === "widget");
5054
- if (sectionOrWidget.length === 0) return;
5062
+ const reusable = manifest.shared.filter((e) => e.type !== "layout");
5063
+ if (reusable.length === 0) return;
5055
5064
  const plannedForPage = plan ? new Set(plan.sharedComponents.filter((c) => c.usedBy.includes(route)).map((c) => c.name)) : null;
5056
- for (const e of sectionOrWidget) {
5065
+ for (const e of reusable) {
5057
5066
  if (plannedForPage && !plannedForPage.has(e.name)) continue;
5058
5067
  const kebab = e.file.replace(/^components\/shared\//, "").replace(/\.tsx$/, "");
5059
5068
  const hasImport = pageCode.includes(`@/components/shared/${kebab}`);
@@ -5298,6 +5307,52 @@ function extractPageNamesFromMessage(message) {
5298
5307
  }
5299
5308
  return pages;
5300
5309
  }
5310
+ function detectExplicitRootPage(message, pageNames) {
5311
+ const lower = message.toLowerCase();
5312
+ const w = "[a-z\u0430-\u044F\u0451A-Z\u0410-\u042F\u04010-9_]";
5313
+ const patterns = [
5314
+ new RegExp(
5315
+ `(?:main|start|home|root|first|entry|primary|\u0441\u0442\u0430\u0440\u0442\u043E\u0432${w}*|\u0433\u043B\u0430\u0432\u043D${w}*|\u043D\u0430\u0447\u0430\u043B\u044C\u043D${w}*)\\s*(?:page|screen|view|\u0441\u0442\u0440\u0430\u043D\u0438\u0446${w}*|\u044D\u043A\u0440\u0430\u043D${w}*)\\s*(?:is|should be|:|\u2014|\u2013|-|\u0431\u0443\u0434\u0435\u0442|\u044D\u0442\u043E)\\s*([a-z\u0430-\u044F\u0451A-Z\u0410-\u042F\u0401\\s]+)`,
5316
+ "i"
5317
+ ),
5318
+ new RegExp(
5319
+ `([a-z\u0430-\u044F\u0451A-Z\u0410-\u042F\u0401\\s]+)\\s*(?:as|for|\u043A\u0430\u043A|\u0432 \u043A\u0430\u0447\u0435\u0441\u0442\u0432\u0435)\\s*(?:the\\s+)?(?:main|start|home|root|primary|\u0441\u0442\u0430\u0440\u0442\u043E\u0432${w}*|\u0433\u043B\u0430\u0432\u043D${w}*)\\s*(?:page|screen|\u0441\u0442\u0440\u0430\u043D\u0438\u0446${w}*)`,
5320
+ "i"
5321
+ ),
5322
+ new RegExp(
5323
+ `(?:start|begin|\u043D\u0430\u0447\u0430\u0442${w}*|\u043D\u0430\u0447\u0438\u043D${w}*)\\s*(?:with|from|\u0441|\u0441\u043E)\\s*(?:a\\s+|an\\s+)?([a-z\u0430-\u044F\u0451A-Z\u0410-\u042F\u0401\\s]+?)(?:\\s+page|\\s+screen|\\s+form|\\s+\u0441\u0442\u0440\u0430\u043D\u0438\u0446${w}*|\\s+\u0444\u043E\u0440\u043C${w}*)?(?:\\s*$|[,.])`,
5324
+ "i"
5325
+ )
5326
+ ];
5327
+ for (const pattern of patterns) {
5328
+ const match = lower.match(pattern);
5329
+ if (match) {
5330
+ const keyword = match[1].trim();
5331
+ const found = pageNames.find(
5332
+ (p) => p.name.toLowerCase().includes(keyword) || keyword.includes(p.name.toLowerCase()) || p.id.includes(keyword.replace(/\s+/g, "-"))
5333
+ );
5334
+ if (found) return found.id;
5335
+ }
5336
+ }
5337
+ return null;
5338
+ }
5339
+ var NON_MARKETING_ROUTES = /* @__PURE__ */ new Set([
5340
+ "/login",
5341
+ "/signin",
5342
+ "/signup",
5343
+ "/register",
5344
+ "/forgot-password",
5345
+ "/reset-password",
5346
+ "/dashboard",
5347
+ "/settings",
5348
+ "/account",
5349
+ "/tasks",
5350
+ "/profile"
5351
+ ]);
5352
+ function isAppOnlyRequest(pageNames) {
5353
+ if (pageNames.length === 0) return false;
5354
+ return pageNames.every((p) => NON_MARKETING_ROUTES.has(p.route) || p.route.startsWith("/dashboard"));
5355
+ }
5301
5356
  function normalizeRequest(request, config2) {
5302
5357
  const changes = request.changes;
5303
5358
  const VALID_TYPES = [
@@ -5567,7 +5622,9 @@ import { existsSync as existsSync14, readFileSync as readFileSync9, readdirSync
5567
5622
  import { resolve as resolve6 } from "path";
5568
5623
  import { z } from "zod";
5569
5624
  import {
5625
+ SharedComponentTypeSchema,
5570
5626
  loadManifest as loadManifest5,
5627
+ saveManifest,
5571
5628
  generateSharedComponent as generateSharedComponent2
5572
5629
  } from "@getcoherent/core";
5573
5630
 
@@ -5688,6 +5745,140 @@ async function pMap(items, fn, concurrency = 3) {
5688
5745
 
5689
5746
  // src/commands/chat/split-generator.ts
5690
5747
  import chalk8 from "chalk";
5748
+
5749
+ // src/utils/reuse-planner.ts
5750
+ var SECTION_TYPE_MAP = {
5751
+ stats: ["data-display", "widget"],
5752
+ metrics: ["data-display", "widget"],
5753
+ kpi: ["data-display", "widget"],
5754
+ list: ["data-display"],
5755
+ table: ["data-display"],
5756
+ items: ["data-display"],
5757
+ form: ["form"],
5758
+ filter: ["form"],
5759
+ search: ["form"],
5760
+ nav: ["navigation"],
5761
+ menu: ["navigation"],
5762
+ tabs: ["navigation"],
5763
+ card: ["widget", "data-display"],
5764
+ grid: ["widget", "data-display"],
5765
+ chart: ["data-display"],
5766
+ graph: ["data-display"],
5767
+ alert: ["feedback"],
5768
+ toast: ["feedback"],
5769
+ banner: ["feedback"]
5770
+ };
5771
+ function sectionToComponentTypes(section) {
5772
+ const lower = section.toLowerCase();
5773
+ const types = /* @__PURE__ */ new Set();
5774
+ for (const [keyword, componentTypes] of Object.entries(SECTION_TYPE_MAP)) {
5775
+ if (lower.includes(keyword)) {
5776
+ componentTypes.forEach((t) => types.add(t));
5777
+ }
5778
+ }
5779
+ return [...types];
5780
+ }
5781
+ function componentFilenameToImportPath(file) {
5782
+ const withoutExt = file.replace(/\.tsx?$/, "");
5783
+ return `@/${withoutExt}`;
5784
+ }
5785
+ function buildReusePlan(input) {
5786
+ const { pageName, sections, manifest } = input;
5787
+ const reuse = [];
5788
+ const usedComponents = /* @__PURE__ */ new Set();
5789
+ for (const section of sections) {
5790
+ const matchingTypes = sectionToComponentTypes(section);
5791
+ if (matchingTypes.length === 0) continue;
5792
+ for (const entry of manifest.shared) {
5793
+ if (usedComponents.has(entry.id)) continue;
5794
+ if (!matchingTypes.includes(entry.type)) continue;
5795
+ reuse.push({
5796
+ component: entry.name,
5797
+ targetSection: section,
5798
+ reason: entry.usedIn.length > 0 ? `Used on ${entry.usedIn.length} page(s)` : `Matches section type (${entry.type})`,
5799
+ importPath: componentFilenameToImportPath(entry.file),
5800
+ usageExample: entry.usageExample || `<${entry.name} />`
5801
+ });
5802
+ usedComponents.add(entry.id);
5803
+ break;
5804
+ }
5805
+ }
5806
+ const reusePatterns = extractCodePatterns(input.existingPageCode, sections);
5807
+ return { pageName, reuse, createNew: [], reusePatterns };
5808
+ }
5809
+ function extractCodePatterns(existingPageCode, _sections) {
5810
+ const patterns = [];
5811
+ const gridPatterns = /* @__PURE__ */ new Map();
5812
+ for (const [route, code] of Object.entries(existingPageCode)) {
5813
+ const gridMatches = code.match(/className="[^"]*grid[^"]*"/g) || [];
5814
+ for (const match of gridMatches) {
5815
+ const cls = match.replace(/className="|"/g, "");
5816
+ if (!gridPatterns.has(cls)) gridPatterns.set(cls, []);
5817
+ gridPatterns.get(cls).push(route);
5818
+ }
5819
+ }
5820
+ for (const [pattern, pages] of gridPatterns) {
5821
+ if (pages.length >= 1 && pattern.includes("grid-cols")) {
5822
+ patterns.push({
5823
+ pattern,
5824
+ sourcePages: pages,
5825
+ targetSection: "Layout grid"
5826
+ });
5827
+ }
5828
+ }
5829
+ return patterns;
5830
+ }
5831
+ function buildReusePlanDirective(plan) {
5832
+ if (plan.reuse.length === 0 && plan.createNew.length === 0 && plan.reusePatterns.length === 0) {
5833
+ return "";
5834
+ }
5835
+ const lines = [`COMPONENT REUSE PLAN FOR THIS PAGE:`];
5836
+ if (plan.reuse.length > 0) {
5837
+ lines.push("", "MUST USE (import these \u2014 do NOT re-implement):");
5838
+ for (const r of plan.reuse) {
5839
+ lines.push(` - ${r.component} from ${r.importPath} \u2014 for "${r.targetSection}" section`);
5840
+ if (r.usageExample) {
5841
+ lines.push(` Example: ${r.usageExample}`);
5842
+ }
5843
+ }
5844
+ }
5845
+ if (plan.createNew.length > 0) {
5846
+ lines.push("", "CREATE NEW (no existing match):");
5847
+ for (const c of plan.createNew) {
5848
+ lines.push(` - ${c.name} \u2014 ${c.reason} (suggest type: ${c.suggestedType})`);
5849
+ }
5850
+ }
5851
+ if (plan.reusePatterns.length > 0) {
5852
+ lines.push("", "LAYOUT PATTERNS (copy from existing pages for visual consistency):");
5853
+ for (const p of plan.reusePatterns) {
5854
+ lines.push(` - ${p.targetSection}: className="${p.pattern}"`);
5855
+ lines.push(` (source: ${p.sourcePages.join(", ")})`);
5856
+ }
5857
+ }
5858
+ return lines.join("\n");
5859
+ }
5860
+ function verifyReusePlan(generatedCode, plan) {
5861
+ const passed = [];
5862
+ const missed = [];
5863
+ for (const entry of plan.reuse) {
5864
+ const isImported = generatedCode.includes(entry.importPath) || generatedCode.includes(`{ ${entry.component} }`) || generatedCode.includes(`{ ${entry.component},`);
5865
+ if (isImported) {
5866
+ passed.push(entry);
5867
+ } else {
5868
+ missed.push(entry);
5869
+ }
5870
+ }
5871
+ let retryDirective;
5872
+ if (missed.length > 0) {
5873
+ const lines = missed.map(
5874
+ (m) => `CRITICAL: Your previous output failed to import ${m.component} from ${m.importPath}. You MUST import and use this component for the "${m.targetSection}" section. Do NOT re-implement it inline.`
5875
+ );
5876
+ retryDirective = lines.join("\n");
5877
+ }
5878
+ return { passed, missed, retryDirective };
5879
+ }
5880
+
5881
+ // src/commands/chat/split-generator.ts
5691
5882
  function buildExistingPagesContext(config2) {
5692
5883
  const pages = config2.pages || [];
5693
5884
  const analyzed = pages.filter((p) => p.pageAnalysis);
@@ -5758,7 +5949,7 @@ function extractStyleContext(pageCode) {
5758
5949
  return `STYLE CONTEXT (match these patterns exactly for visual consistency with the anchor page):
5759
5950
  ${lines.map((l) => ` - ${l}`).join("\n")}`;
5760
5951
  }
5761
- var VALID_NAV_TYPES = /* @__PURE__ */ new Set(["header", "sidebar", "both"]);
5952
+ var VALID_NAV_TYPES = /* @__PURE__ */ new Set(["header", "sidebar", "both", "none"]);
5762
5953
  function parseNavTypeFromPlan(planResult) {
5763
5954
  const nav = planResult.navigation;
5764
5955
  if (nav && typeof nav.type === "string" && VALID_NAV_TYPES.has(nav.type)) {
@@ -5882,6 +6073,52 @@ function readExistingAppPageForReference(projectRoot, plan) {
5882
6073
  }
5883
6074
  return null;
5884
6075
  }
6076
+ function buildLayoutNote(layoutType) {
6077
+ switch (layoutType) {
6078
+ case "sidebar":
6079
+ return "This page uses a SIDEBAR layout. The sidebar navigation is already rendered by the group layout. Do NOT create your own sidebar or side navigation. Start with the main content area directly. The page content appears to the right of the sidebar.";
6080
+ case "both":
6081
+ return "This page has both a sidebar and a header rendered by the group layout. Do NOT include any site-wide header, nav, sidebar, or footer in this page. Start with the main content directly.";
6082
+ case "none":
6083
+ return "This page has no shared navigation from the layout. Include any needed navigation within the page itself.";
6084
+ default:
6085
+ return "Header and Footer are shared components rendered by the root layout. Do NOT include any site-wide <header>, <nav>, or <footer> in this page. Start with the main content directly.";
6086
+ }
6087
+ }
6088
+ function buildAnchorPagePrompt(homePage, message, allPagesList, allRoutes, plan) {
6089
+ const pageType = detectPageType(homePage.name) || detectPageType(homePage.route);
6090
+ const authPageTypes = /* @__PURE__ */ new Set(["login", "register", "reset-password"]);
6091
+ const isAuth = isAuthRoute(homePage.route) || isAuthRoute(homePage.name) || authPageTypes.has(pageType || "");
6092
+ if (isAuth) {
6093
+ return `Create ONE page called "${homePage.name}" at route "${homePage.route}". Context: ${message}. This is the application's entry point \u2014 a clean, centered authentication form. Generate complete pageCode. Do NOT include site-wide <header>, <nav>, or <footer> \u2014 this page has its own minimal layout. Make it visually polished with proper form validation UI \u2014 this page sets the design direction for the entire site. Do not generate other pages.`;
6094
+ }
6095
+ const groupLayout = plan?.groups.find((g) => g.pages.includes(homePage.route))?.layout;
6096
+ if (groupLayout === "sidebar" || pageType === "dashboard") {
6097
+ return `Create ONE page called "${homePage.name}" at route "${homePage.route}". Context: ${message}. This REPLACES the default placeholder page \u2014 generate a complete application page. Generate complete pageCode. Do NOT include a sidebar or top navigation \u2014 these are handled by the layout. Focus on the main content area. Make it visually polished \u2014 this page sets the design direction for the entire site. Do not generate other pages.`;
6098
+ }
6099
+ return `Create ONE page called "${homePage.name}" at route "${homePage.route}". Context: ${message}. This REPLACES the default placeholder page \u2014 generate a complete, content-rich landing page for the project described above. Generate complete pageCode. Include a branded site-wide <header> with navigation links to ALL these pages: ${allPagesList}. Use these EXACT routes in navigation: ${allRoutes}. Include a <footer> at the bottom. Make it visually polished \u2014 this page sets the design direction for the entire site. Do not generate other pages.`;
6100
+ }
6101
+ function getGroupLayoutForRoute(route, plan) {
6102
+ if (!plan) return void 0;
6103
+ const group = plan.groups.find((g) => g.pages.includes(route));
6104
+ return group?.layout;
6105
+ }
6106
+ var manifestLock = Promise.resolve();
6107
+ async function updateManifestSafe(projectRoot, fn) {
6108
+ const timeoutMs = 5e3;
6109
+ const update = manifestLock.then(async () => {
6110
+ const m = await loadManifest5(projectRoot);
6111
+ const updated = fn(m);
6112
+ await saveManifest(projectRoot, updated);
6113
+ });
6114
+ manifestLock = update.catch(() => {
6115
+ });
6116
+ await Promise.race([
6117
+ update,
6118
+ new Promise((_, reject) => setTimeout(() => reject(new Error("manifest sync timeout")), timeoutMs))
6119
+ ]).catch(() => {
6120
+ });
6121
+ }
5885
6122
  async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts) {
5886
6123
  let pageNames = [];
5887
6124
  spinner.start("Phase 1/6 \u2014 Planning pages...");
@@ -5924,7 +6161,11 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
5924
6161
  (p) => p.id !== "home" && p.id !== "new" && p.route !== "/"
5925
6162
  );
5926
6163
  const isFreshProject = userPages.length === 0;
5927
- if (isFreshProject || impliesFullWebsite(message)) {
6164
+ const explicitRootId = detectExplicitRootPage(message, pageNames);
6165
+ if (explicitRootId) {
6166
+ const rootPage = pageNames.find((p) => p.id === explicitRootId);
6167
+ if (rootPage) rootPage.route = "/";
6168
+ } else if (!isAppOnlyRequest(pageNames) && (isFreshProject || impliesFullWebsite(message))) {
5928
6169
  pageNames.unshift({ name: "Home", id: "home", route: "/" });
5929
6170
  }
5930
6171
  }
@@ -5943,14 +6184,16 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
5943
6184
  spinner.start("Phase 2/6 \u2014 Generating architecture plan...");
5944
6185
  try {
5945
6186
  const ai = await createAIProvider(provider ?? "auto");
5946
- const layoutHint = modCtx.config.navigation?.type || null;
5947
- const { plan: generatedPlan, warnings: planWarnings } = await generateArchitecturePlan(
5948
- pageNames,
5949
- message,
5950
- ai,
5951
- layoutHint
5952
- );
5953
- plan = generatedPlan;
6187
+ const cachedPlan = loadPlan(parseOpts.projectRoot);
6188
+ let planWarnings = [];
6189
+ if (cachedPlan) {
6190
+ plan = await updateArchitecturePlan(cachedPlan, pageNames, message, ai);
6191
+ } else {
6192
+ const layoutHint = modCtx.config.navigation?.type || null;
6193
+ const result = await generateArchitecturePlan(pageNames, message, ai, layoutHint);
6194
+ plan = result.plan;
6195
+ planWarnings = result.warnings;
6196
+ }
5954
6197
  if (plan) {
5955
6198
  const groupsSummary = plan.groups.map((g) => `${g.id} (${g.layout}, ${g.pages.length} pages)`).join(", ");
5956
6199
  const sharedSummary = plan.sharedComponents.length > 0 ? plan.sharedComponents.map((c) => `${c.name} \u2192 ${c.usedBy.join(", ")}`).join(" | ") : "";
@@ -6001,12 +6244,8 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
6001
6244
  if (!reusedExistingAnchor) {
6002
6245
  spinner.start(`Phase 3/6 \u2014 Generating ${homePage.name} page (sets design direction)...`);
6003
6246
  try {
6004
- const homeResult = await parseModification(
6005
- `Create ONE page called "${homePage.name}" at route "${homePage.route}". Context: ${message}. This REPLACES the default placeholder page \u2014 generate a complete, content-rich landing page for the project described above. Generate complete pageCode. Include a branded site-wide <header> with navigation links to ALL these pages: ${allPagesList}. Use these EXACT routes in navigation: ${allRoutes}. Include a <footer> at the bottom. Make it visually polished \u2014 this page sets the design direction for the entire site. Do not generate other pages.`,
6006
- modCtx,
6007
- provider,
6008
- parseOpts
6009
- );
6247
+ const anchorPrompt = buildAnchorPagePrompt(homePage, message, allPagesList, allRoutes, plan);
6248
+ const homeResult = await parseModification(anchorPrompt, modCtx, provider, parseOpts);
6010
6249
  const codePage = homeResult.requests.find((r) => r.type === "add-page");
6011
6250
  if (codePage) {
6012
6251
  homeRequest = codePage;
@@ -6036,7 +6275,7 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
6036
6275
  if (plan && plan.sharedComponents.length > 0) {
6037
6276
  spinner.start(`Phase 4.5/6 \u2014 Generating ${plan.sharedComponents.length} shared components from plan...`);
6038
6277
  try {
6039
- const { generateSharedComponentsFromPlan } = await import("./plan-generator-H55WEIY2.js");
6278
+ const { generateSharedComponentsFromPlan } = await import("./plan-generator-ITHYNYJI.js");
6040
6279
  const generated = await generateSharedComponentsFromPlan(
6041
6280
  plan,
6042
6281
  styleContext,
@@ -6078,7 +6317,6 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
6078
6317
  return { requests: homeRequest ? [homeRequest] : [], plan };
6079
6318
  }
6080
6319
  spinner.start(`Phase 5/6 \u2014 Generating ${remainingPages.length} pages in parallel...`);
6081
- const sharedLayoutNote = "Header and Footer are shared components rendered by the root layout. Do NOT include any site-wide <header>, <nav>, or <footer> in this page. Start with the main content directly.";
6082
6320
  const sharedComponentsNote = buildSharedComponentsNote(parseOpts.sharedComponentsSummary);
6083
6321
  const currentManifest = projectRoot ? await loadManifest5(projectRoot) : null;
6084
6322
  const routeNote = `EXISTING ROUTES in this project: ${allRoutes}. All internal links MUST point to one of these routes. If a target doesn't exist, use href="#".`;
@@ -6092,6 +6330,23 @@ ${existingAppPageCode}
6092
6330
  \`\`\`
6093
6331
  ` : "";
6094
6332
  const existingPagesContext = buildExistingPagesContext(modCtx.config);
6333
+ const existingPageCode = {};
6334
+ if (projectRoot) {
6335
+ const appDir = resolve6(projectRoot, "app");
6336
+ if (existsSync14(appDir)) {
6337
+ const pageFiles = readdirSync2(appDir, { recursive: true }).filter(
6338
+ (f) => typeof f === "string" && f.endsWith("page.tsx")
6339
+ );
6340
+ for (const pf of pageFiles) {
6341
+ try {
6342
+ const code = readFileSync9(resolve6(appDir, pf), "utf-8");
6343
+ const route = "/" + pf.replace(/\/page\.tsx$/, "").replace(/\(.*?\)\//g, "");
6344
+ existingPageCode[route === "/" ? "/" : route] = code;
6345
+ } catch {
6346
+ }
6347
+ }
6348
+ }
6349
+ }
6095
6350
  const AI_CONCURRENCY = 3;
6096
6351
  let phase5Done = 0;
6097
6352
  const remainingRequests = await pMap(
@@ -6101,15 +6356,45 @@ ${existingAppPageCode}
6101
6356
  const pageType = plan ? getPageType(route, plan) : inferPageTypeFromRoute(route);
6102
6357
  const designConstraints = getDesignQualityForType(pageType);
6103
6358
  const authNote = isAuth ? 'For this auth page: the auth layout already provides centering (flex items-center justify-center min-h-svh). Do NOT add your own centering wrapper or min-h-svh. Just output a div with className="w-full max-w-md" containing the Card. Do NOT use section containers or full-width wrappers.' : void 0;
6359
+ const layoutForPage = getGroupLayoutForRoute(route, plan);
6360
+ const layoutNote = buildLayoutNote(layoutForPage);
6104
6361
  const tieredNote = currentManifest ? buildTieredComponentsPrompt(currentManifest, pageType) : void 0;
6362
+ const pageKey = route.replace(/^\//, "") || "home";
6363
+ const pageSections = plan?.pageNotes?.[pageKey]?.sections || [];
6364
+ let reusePlanDirective = "";
6365
+ let currentReusePlan = null;
6366
+ if (currentManifest && currentManifest.shared.length > 0) {
6367
+ try {
6368
+ currentReusePlan = buildReusePlan({
6369
+ pageName: name,
6370
+ pageType,
6371
+ sections: pageSections,
6372
+ manifest: currentManifest,
6373
+ existingPageCode,
6374
+ userRequest: message
6375
+ });
6376
+ reusePlanDirective = buildReusePlanDirective(currentReusePlan);
6377
+ if (currentReusePlan.reuse.length > 0 || currentReusePlan.createNew.length > 0) {
6378
+ const parts = [];
6379
+ if (currentReusePlan.reuse.length > 0)
6380
+ parts.push(`REUSE: ${currentReusePlan.reuse.map((r) => r.component).join(", ")}`);
6381
+ if (currentReusePlan.createNew.length > 0)
6382
+ parts.push(`CREATE: ${currentReusePlan.createNew.map((c) => c.name).join(", ")}`);
6383
+ if (currentReusePlan.reusePatterns.length > 0)
6384
+ parts.push(`${currentReusePlan.reusePatterns.length} pattern(s)`);
6385
+ console.log(chalk8.dim(` \u{1F504} Reuse Plan for "${name}": ${parts.join(" | ")}`));
6386
+ }
6387
+ } catch {
6388
+ }
6389
+ }
6105
6390
  const prompt = [
6106
6391
  `Create ONE page called "${name}" at route "${route}".`,
6107
6392
  `Context: ${message}.`,
6108
6393
  `Generate complete pageCode for this single page only. Do not generate other pages.`,
6109
6394
  `PAGE TYPE: ${pageType}`,
6110
6395
  designConstraints,
6111
- sharedLayoutNote,
6112
- tieredNote || sharedComponentsNote,
6396
+ layoutNote,
6397
+ reusePlanDirective || tieredNote || sharedComponentsNote,
6113
6398
  routeNote,
6114
6399
  alignmentNote,
6115
6400
  authNote,
@@ -6122,7 +6407,47 @@ ${existingAppPageCode}
6122
6407
  const result = await parseModification(prompt, modCtx, provider, parseOpts);
6123
6408
  phase5Done++;
6124
6409
  spinner.text = `Phase 5/6 \u2014 ${phase5Done}/${remainingPages.length} pages generated...`;
6125
- const codePage = result.requests.find((r) => r.type === "add-page");
6410
+ let codePage = result.requests.find((r) => r.type === "add-page");
6411
+ if (currentReusePlan && currentReusePlan.reuse.length > 0 && codePage) {
6412
+ const pageCode = codePage.changes?.pageCode;
6413
+ if (pageCode) {
6414
+ const verification = verifyReusePlan(pageCode, currentReusePlan);
6415
+ if (verification.passed.length > 0) {
6416
+ console.log(
6417
+ chalk8.dim(` \u2713 Reuse verified for "${name}": ${verification.passed.map((p) => p.component).join(", ")}`)
6418
+ );
6419
+ }
6420
+ if (verification.missed.length > 0 && verification.retryDirective) {
6421
+ console.log(
6422
+ chalk8.yellow(
6423
+ ` \u26A0 Missed reuse in "${name}": ${verification.missed.map((m) => m.component).join(", ")} \u2014 retrying...`
6424
+ )
6425
+ );
6426
+ try {
6427
+ const retryPrompt = [prompt, verification.retryDirective].join("\n\n");
6428
+ const retryResult = await parseModification(retryPrompt, modCtx, provider, parseOpts);
6429
+ const retryPage = retryResult.requests.find((r) => r.type === "add-page");
6430
+ if (retryPage) codePage = retryPage;
6431
+ } catch {
6432
+ }
6433
+ }
6434
+ }
6435
+ }
6436
+ if (projectRoot && codePage && currentManifest) {
6437
+ const finalPageCode = codePage.changes?.pageCode;
6438
+ if (finalPageCode) {
6439
+ await updateManifestSafe(projectRoot, (m) => {
6440
+ const updatedShared = m.shared.map((entry) => {
6441
+ const isUsed = finalPageCode.includes(`{ ${entry.name} }`) || finalPageCode.includes(`{ ${entry.name},`);
6442
+ if (isUsed && !entry.usedIn.includes(route)) {
6443
+ return { ...entry, usedIn: [...entry.usedIn, route] };
6444
+ }
6445
+ return entry;
6446
+ });
6447
+ return { ...m, shared: updatedShared };
6448
+ });
6449
+ }
6450
+ }
6126
6451
  return codePage || { type: "add-page", target: "new", changes: { id, name, route } };
6127
6452
  } catch {
6128
6453
  phase5Done++;
@@ -6170,7 +6495,7 @@ ${existingAppPageCode}
6170
6495
  }
6171
6496
  var SharedExtractionItemSchema = z.object({
6172
6497
  name: z.string().min(2).max(50),
6173
- type: z.enum(["section", "widget"]),
6498
+ type: SharedComponentTypeSchema,
6174
6499
  description: z.string().max(200).default(""),
6175
6500
  propsInterface: z.string().default("{}"),
6176
6501
  code: z.string()
@@ -6216,7 +6541,8 @@ async function extractSharedComponents(homePageCode, projectRoot, aiProvider) {
6216
6541
  const provider = getComponentProvider();
6217
6542
  for (const item of filtered) {
6218
6543
  try {
6219
- const { code: fixedCode } = await autoFixCode(item.code);
6544
+ let { code: fixedCode } = await autoFixCode(item.code);
6545
+ fixedCode = fixedCode.replace(/export default function (\w+)/g, "export function $1");
6220
6546
  const shadcnImports = [...fixedCode.matchAll(/from\s+["']@\/components\/ui\/(.+?)["']/g)];
6221
6547
  for (const match of shadcnImports) {
6222
6548
  await provider.installComponent(match[1], projectRoot);
@@ -6284,7 +6610,7 @@ import chalk11 from "chalk";
6284
6610
  import {
6285
6611
  getTemplateForPageType,
6286
6612
  loadManifest as loadManifest6,
6287
- saveManifest,
6613
+ saveManifest as saveManifest2,
6288
6614
  updateUsedIn,
6289
6615
  findSharedComponentByIdOrName,
6290
6616
  generateSharedComponent as generateSharedComponent4
@@ -6394,7 +6720,9 @@ async function regeneratePage(pageId, config2, projectRoot) {
6394
6720
  const code = await generator.generate(page, appType);
6395
6721
  const route = page.route || "/";
6396
6722
  const isAuth = isAuthRoute(route) || isAuthRoute(page.name || page.id || "");
6397
- const filePath = routeToFsPath(projectRoot, route, isAuth);
6723
+ const { loadPlan: loadPlanForPath } = await import("./plan-generator-ITHYNYJI.js");
6724
+ const planForPath = loadPlanForPath(projectRoot);
6725
+ const filePath = routeToFsPath(projectRoot, route, planForPath || isAuth);
6398
6726
  await mkdir3(dirname5(filePath), { recursive: true });
6399
6727
  await writeFile(filePath, code);
6400
6728
  }
@@ -6409,7 +6737,9 @@ async function canOverwriteShared(projectRoot, componentFile, storedHashes) {
6409
6737
  }
6410
6738
  return !edited;
6411
6739
  }
6412
- async function regenerateLayout(config2, projectRoot, options = { navChanged: false }) {
6740
+ async function regenerateLayout(config2, projectRoot, options = {
6741
+ navChanged: false
6742
+ }) {
6413
6743
  const appType = config2.settings.appType || "multi-page";
6414
6744
  const generator = new PageGenerator(config2);
6415
6745
  const initialized = config2.settings.initialized !== false;
@@ -6465,7 +6795,7 @@ async function regenerateLayout(config2, projectRoot, options = { navChanged: fa
6465
6795
  try {
6466
6796
  await integrateSharedLayoutIntoRootLayout2(projectRoot);
6467
6797
  await ensureAuthRouteGroup(projectRoot);
6468
- await ensureAppRouteGroupLayout(projectRoot, config2.navigation?.type, options.navChanged);
6798
+ await ensureAppRouteGroupLayout(projectRoot, config2.navigation?.type, options.navChanged, options.groupLayouts);
6469
6799
  } catch (err) {
6470
6800
  if (process.env.COHERENT_DEBUG === "1") {
6471
6801
  console.log(chalk9.dim("Layout integration warning:", err));
@@ -6494,18 +6824,20 @@ async function scanAndInstallSharedDeps(projectRoot) {
6494
6824
  }
6495
6825
  return [...new Set(installed)];
6496
6826
  }
6497
- async function ensureAppRouteGroupLayout(projectRoot, navType, forceUpdate = false) {
6827
+ async function ensureAppRouteGroupLayout(projectRoot, navType, forceUpdate = false, groupLayouts) {
6828
+ const effectiveNavType = groupLayouts?.["app"] || navType;
6498
6829
  const layoutPath = resolve7(projectRoot, "app", "(app)", "layout.tsx");
6499
6830
  if (existsSync15(layoutPath) && !forceUpdate) return;
6500
6831
  const { mkdir: mkdirAsync } = await import("fs/promises");
6501
6832
  await mkdirAsync(resolve7(projectRoot, "app", "(app)"), { recursive: true });
6502
- const code = buildAppLayoutCode(navType);
6833
+ const code = buildAppLayoutCode(effectiveNavType);
6503
6834
  await writeFile(layoutPath, code);
6504
6835
  }
6505
6836
  function buildAppLayoutCode(navType) {
6506
6837
  const hasSidebar = navType === "sidebar" || navType === "both";
6507
6838
  if (hasSidebar) {
6508
- return `import { Sidebar } from '@/components/shared/sidebar'
6839
+ return `import { AppSidebar } from '@/components/shared/sidebar'
6840
+ import { SidebarProvider, SidebarInset } from '@/components/ui/sidebar'
6509
6841
 
6510
6842
  export default function AppLayout({
6511
6843
  children,
@@ -6513,12 +6845,14 @@ export default function AppLayout({
6513
6845
  children: React.ReactNode
6514
6846
  }) {
6515
6847
  return (
6516
- <div className="flex min-h-[calc(100vh-3.5rem)]">
6517
- <Sidebar />
6518
- <main className="flex-1 px-4 sm:px-6 lg:px-8 py-6">
6519
- {children}
6520
- </main>
6521
- </div>
6848
+ <SidebarProvider>
6849
+ <AppSidebar />
6850
+ <SidebarInset>
6851
+ <main className="flex-1 px-4 sm:px-6 lg:px-8 py-6">
6852
+ {children}
6853
+ </main>
6854
+ </SidebarInset>
6855
+ </SidebarProvider>
6522
6856
  )
6523
6857
  }
6524
6858
  `;
@@ -6538,7 +6872,8 @@ export default function AppLayout({
6538
6872
  }
6539
6873
  function buildGroupLayoutCode(layout, _pages) {
6540
6874
  if (layout === "sidebar" || layout === "both") {
6541
- return `import { Sidebar } from '@/components/shared/sidebar'
6875
+ return `import { AppSidebar } from '@/components/shared/sidebar'
6876
+ import { SidebarProvider, SidebarInset } from '@/components/ui/sidebar'
6542
6877
 
6543
6878
  export default function GroupLayout({
6544
6879
  children,
@@ -6546,12 +6881,14 @@ export default function GroupLayout({
6546
6881
  children: React.ReactNode
6547
6882
  }) {
6548
6883
  return (
6549
- <div className="flex min-h-[calc(100vh-3.5rem)]">
6550
- <Sidebar />
6551
- <main className="flex-1 px-4 sm:px-6 lg:px-8 py-6">
6552
- {children}
6553
- </main>
6554
- </div>
6884
+ <SidebarProvider>
6885
+ <AppSidebar />
6886
+ <SidebarInset>
6887
+ <main className="flex-1 px-4 sm:px-6 lg:px-8 py-6">
6888
+ {children}
6889
+ </main>
6890
+ </SidebarInset>
6891
+ </SidebarProvider>
6555
6892
  )
6556
6893
  }
6557
6894
  `;
@@ -6583,15 +6920,32 @@ export default function GroupLayout({
6583
6920
  }
6584
6921
  `;
6585
6922
  }
6586
- async function ensurePlanGroupLayouts(projectRoot, plan) {
6923
+ async function ensurePlanGroupLayouts(projectRoot, plan, storedHashes = {}, config2) {
6587
6924
  const { mkdir: mkdirAsync } = await import("fs/promises");
6925
+ const { createHash: createHash2 } = await import("crypto");
6588
6926
  for (const group of plan.groups) {
6589
6927
  const groupDir = resolve7(projectRoot, "app", `(${group.id})`);
6590
6928
  await mkdirAsync(groupDir, { recursive: true });
6591
6929
  const layoutPath = resolve7(groupDir, "layout.tsx");
6930
+ const relPath = `app/(${group.id})/layout.tsx`;
6931
+ if (existsSync15(layoutPath)) {
6932
+ const currentContent = readFileSync10(layoutPath, "utf-8");
6933
+ const currentHash = createHash2("md5").update(currentContent).digest("hex");
6934
+ const storedHash = storedHashes[relPath];
6935
+ if (storedHash && storedHash !== currentHash) {
6936
+ continue;
6937
+ }
6938
+ }
6592
6939
  const code = buildGroupLayoutCode(group.layout, group.pages);
6593
6940
  await writeFile(layoutPath, code);
6594
6941
  }
6942
+ if (config2) {
6943
+ const layouts = {};
6944
+ for (const group of plan.groups) {
6945
+ layouts[group.id] = group.layout;
6946
+ }
6947
+ config2.groupLayouts = layouts;
6948
+ }
6595
6949
  }
6596
6950
  async function regenerateFiles(modified, config2, projectRoot, options = { navChanged: false }) {
6597
6951
  const componentIds = /* @__PURE__ */ new Set();
@@ -7058,7 +7412,8 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7058
7412
  modified: []
7059
7413
  };
7060
7414
  }
7061
- const pageFilePath = routeToFsPath(projectRoot, route, false);
7415
+ const readPlan = projectRoot ? loadPlan(projectRoot) : null;
7416
+ const pageFilePath = routeToFsPath(projectRoot, route, readPlan || false);
7062
7417
  let pageCode;
7063
7418
  try {
7064
7419
  pageCode = await readFile(pageFilePath);
@@ -7093,7 +7448,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7093
7448
  const filePathRel = routePath ? `app/${routePath}/page.tsx` : "app/page.tsx";
7094
7449
  if (!usedIn.includes(filePathRel)) {
7095
7450
  const nextManifest = updateUsedIn(manifest, resolved.id, [...usedIn, filePathRel]);
7096
- await saveManifest(projectRoot, nextManifest);
7451
+ await saveManifest2(projectRoot, nextManifest);
7097
7452
  }
7098
7453
  printLinkSharedReport({
7099
7454
  sharedId: resolved.id,
@@ -7194,7 +7549,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7194
7549
  }
7195
7550
  const manifest = await loadManifest6(projectRoot);
7196
7551
  const nextManifest = updateUsedIn(manifest, created.id, usedInFiles);
7197
- await saveManifest(projectRoot, nextManifest);
7552
+ await saveManifest2(projectRoot, nextManifest);
7198
7553
  printPromoteAndLinkReport({
7199
7554
  id: created.id,
7200
7555
  name: created.name,
@@ -7341,13 +7696,13 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7341
7696
  if (isAuth) {
7342
7697
  await ensureAuthRouteGroup(projectRoot);
7343
7698
  }
7344
- const filePath = routeToFsPath(projectRoot, route, isAuth);
7699
+ const currentPlan = projectRoot ? loadPlan(projectRoot) : null;
7700
+ const filePath = routeToFsPath(projectRoot, route, currentPlan || isAuth);
7345
7701
  await mkdir4(dirname6(filePath), { recursive: true });
7346
7702
  const { fixedCode, fixes: postFixes } = await validateAndFixGeneratedCode(projectRoot, finalPageCode, {
7347
7703
  isPage: true
7348
7704
  });
7349
7705
  let codeToWrite = fixedCode;
7350
- const currentPlan = projectRoot ? loadPlan(projectRoot) : null;
7351
7706
  const autoFixCtx = route ? {
7352
7707
  currentRoute: route,
7353
7708
  knownRoutes: dsm.getConfig().pages.map((p) => p.route).filter(Boolean),
@@ -7413,44 +7768,55 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7413
7768
  });
7414
7769
  let issues = validatePageQuality(codeToWrite, void 0, qualityPageType);
7415
7770
  const errors = issues.filter((i) => i.severity === "error");
7416
- if (errors.length >= 2 && aiProvider) {
7771
+ const MAX_QUALITY_FIX_ATTEMPTS = 2;
7772
+ let currentErrors = errors;
7773
+ for (let attempt = 0; attempt < MAX_QUALITY_FIX_ATTEMPTS && currentErrors.length > 0; attempt++) {
7774
+ if (!aiProvider) break;
7417
7775
  console.log(
7418
- chalk11.yellow(`
7419
- \u{1F504} ${errors.length} quality errors \u2014 attempting AI fix for ${page.name || page.id}...`)
7776
+ chalk11.yellow(
7777
+ `
7778
+ \u{1F504} ${currentErrors.length} quality errors \u2014 attempting AI fix${attempt > 0 ? ` (retry ${attempt + 1})` : ""} for ${page.name || page.id}...`
7779
+ )
7420
7780
  );
7421
7781
  try {
7422
7782
  const ai = await createAIProvider(aiProvider);
7423
- if (ai.editPageCode) {
7424
- const errorList = errors.map((e) => `Line ${e.line}: [${e.type}] ${e.message}`).join("\n");
7425
- const instruction = `Fix these quality issues:
7783
+ if (!ai.editPageCode) break;
7784
+ const errorList = currentErrors.map((e) => `Line ${e.line}: [${e.type}] ${e.message}`).join("\n");
7785
+ const instruction = `Fix these quality issues:
7426
7786
  ${errorList}
7427
7787
 
7428
7788
  Rules:
7429
7789
  - Replace raw Tailwind colors (bg-emerald-500, text-zinc-400, etc.) with semantic tokens (bg-primary, text-muted-foreground, bg-muted, etc.)
7790
+ - Replace placeholder content ("Lorem ipsum", "John Doe", "user@example.com") with realistic contextual content
7430
7791
  - Ensure heading hierarchy (h1 \u2192 h2 \u2192 h3, no skipping)
7431
7792
  - Add Label components for form inputs
7432
7793
  - Keep all existing functionality and layout intact`;
7433
- const fixedCode2 = await ai.editPageCode(codeToWrite, instruction, page.name || page.id || "Page");
7434
- if (fixedCode2 && fixedCode2.length > 100 && /export\s+default/.test(fixedCode2)) {
7435
- const recheck = validatePageQuality(fixedCode2, void 0, qualityPageType);
7436
- const recheckErrors = recheck.filter((i) => i.severity === "error");
7437
- if (recheckErrors.length < errors.length) {
7438
- codeToWrite = fixedCode2;
7439
- const { code: reFixed, fixes: reFixes } = await autoFixCode(codeToWrite, autoFixCtx);
7440
- if (reFixes.length > 0) {
7441
- codeToWrite = reFixed;
7442
- postFixes.push(...reFixes);
7443
- }
7444
- await writeFile(filePath, codeToWrite);
7445
- issues = validatePageQuality(codeToWrite, void 0, qualityPageType);
7446
- const finalErrors = issues.filter((i) => i.severity === "error").length;
7447
- console.log(chalk11.green(` \u2714 Quality fix: ${errors.length} \u2192 ${finalErrors} errors`));
7794
+ const fixedCode2 = await ai.editPageCode(codeToWrite, instruction, page.name || page.id || "Page");
7795
+ if (fixedCode2 && fixedCode2.length > 100 && /export\s+default/.test(fixedCode2)) {
7796
+ const recheck = validatePageQuality(fixedCode2, void 0, qualityPageType);
7797
+ const recheckErrors = recheck.filter((i) => i.severity === "error");
7798
+ if (recheckErrors.length < currentErrors.length) {
7799
+ codeToWrite = fixedCode2;
7800
+ const { code: reFixed, fixes: reFixes } = await autoFixCode(codeToWrite, autoFixCtx);
7801
+ if (reFixes.length > 0) {
7802
+ codeToWrite = reFixed;
7803
+ postFixes.push(...reFixes);
7448
7804
  }
7805
+ await writeFile(filePath, codeToWrite);
7806
+ currentErrors = recheckErrors;
7807
+ console.log(chalk11.green(` \u2714 Quality fix: ${errors.length} \u2192 ${currentErrors.length} errors`));
7808
+ if (currentErrors.length === 0) break;
7809
+ } else {
7810
+ break;
7449
7811
  }
7812
+ } else {
7813
+ break;
7450
7814
  }
7451
7815
  } catch {
7816
+ break;
7452
7817
  }
7453
7818
  }
7819
+ issues = validatePageQuality(codeToWrite, void 0, qualityPageType);
7454
7820
  const report = formatIssues(issues);
7455
7821
  if (report) {
7456
7822
  console.log(chalk11.yellow(`
@@ -7496,7 +7862,8 @@ Rules:
7496
7862
  if (pageDef?.route) {
7497
7863
  const route = pageDef.route;
7498
7864
  const isAuth = isAuthRoute(route) || isAuthRoute(pageDef.name || pageDef.id || "");
7499
- const absPath = routeToFsPath(projectRoot, route, isAuth);
7865
+ const updatePlan = projectRoot ? loadPlan(projectRoot) : null;
7866
+ const absPath = routeToFsPath(projectRoot, route, updatePlan || isAuth);
7500
7867
  if (!resolvedPageCode && instruction) {
7501
7868
  let currentCode;
7502
7869
  try {
@@ -7630,7 +7997,54 @@ ${pagesCtx}`
7630
7997
  allShared: manifestForAudit.shared,
7631
7998
  layoutShared: manifestForAudit.shared.filter((c) => c.type === "layout")
7632
7999
  });
7633
- const issues = validatePageQuality(codeToWrite, void 0, qualityPageType2);
8000
+ let issues = validatePageQuality(codeToWrite, void 0, qualityPageType2);
8001
+ let qualityErrors = issues.filter((i) => i.severity === "error");
8002
+ const MAX_UPDATE_QUALITY_FIX_ATTEMPTS = 2;
8003
+ for (let attempt = 0; attempt < MAX_UPDATE_QUALITY_FIX_ATTEMPTS && qualityErrors.length > 0; attempt++) {
8004
+ if (!aiProvider) break;
8005
+ try {
8006
+ const ai = await createAIProvider(aiProvider);
8007
+ if (!ai.editPageCode) break;
8008
+ const errorList = qualityErrors.map((e) => `Line ${e.line}: [${e.type}] ${e.message}`).join("\n");
8009
+ const fixInstruction = `Fix these quality issues:
8010
+ ${errorList}
8011
+
8012
+ Rules:
8013
+ - Replace raw Tailwind colors with semantic tokens (bg-primary, text-muted-foreground, etc.)
8014
+ - Replace placeholder content with realistic contextual content
8015
+ - Ensure heading hierarchy
8016
+ - Keep all existing functionality and layout intact`;
8017
+ const qFixedCode = await ai.editPageCode(
8018
+ codeToWrite,
8019
+ fixInstruction,
8020
+ pageDef.name || pageDef.id || "Page"
8021
+ );
8022
+ if (qFixedCode && qFixedCode.length > 100 && /export\s+(default\s+)?function/.test(qFixedCode)) {
8023
+ const recheck = validatePageQuality(qFixedCode, void 0, qualityPageType2);
8024
+ const recheckErrors = recheck.filter((i) => i.severity === "error");
8025
+ if (recheckErrors.length < qualityErrors.length) {
8026
+ codeToWrite = qFixedCode;
8027
+ const { code: reFixed } = await autoFixCode(codeToWrite, autoFixCtx2);
8028
+ codeToWrite = reFixed;
8029
+ await writeFile(absPath, codeToWrite);
8030
+ qualityErrors = recheckErrors;
8031
+ console.log(
8032
+ chalk11.green(
8033
+ ` \u2714 Quality fix: ${qualityErrors.length + (qualityErrors.length - recheckErrors.length)} \u2192 ${recheckErrors.length} errors`
8034
+ )
8035
+ );
8036
+ if (qualityErrors.length === 0) break;
8037
+ } else {
8038
+ break;
8039
+ }
8040
+ } else {
8041
+ break;
8042
+ }
8043
+ } catch {
8044
+ break;
8045
+ }
8046
+ }
8047
+ issues = validatePageQuality(codeToWrite, void 0, qualityPageType2);
7634
8048
  const report = formatIssues(issues);
7635
8049
  if (report) {
7636
8050
  console.log(chalk11.yellow(`
@@ -8056,7 +8470,7 @@ async function chatCommand(message, options) {
8056
8470
  if (options.newComponent) {
8057
8471
  const componentName = options.newComponent;
8058
8472
  spinner.start(`Creating shared component: ${componentName}...`);
8059
- const { createAIProvider: createAIProvider2 } = await import("./ai-provider-HUQO64P3.js");
8473
+ const { createAIProvider: createAIProvider2 } = await import("./ai-provider-CGSIYFZT.js");
8060
8474
  const { generateSharedComponent: generateSharedComponent7 } = await import("@getcoherent/core");
8061
8475
  const { autoFixCode: autoFixCode2 } = await import("./quality-validator-3K5BMJSR.js");
8062
8476
  const { extractPropsInterface, extractDependencies } = await import("./component-extractor-VYJLT5NR.js");
@@ -8102,7 +8516,7 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
8102
8516
  type: classifications[0].type,
8103
8517
  description: classifications[0].description || message
8104
8518
  });
8105
- await saveManifest2(projectRoot, manifest2);
8519
+ await saveManifest3(projectRoot, manifest2);
8106
8520
  }
8107
8521
  } catch {
8108
8522
  }
@@ -8171,7 +8585,7 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
8171
8585
  if (validShared.length !== manifest.shared.length) {
8172
8586
  const cleaned = manifest.shared.length - validShared.length;
8173
8587
  manifest = { ...manifest, shared: validShared };
8174
- await saveManifest2(project.root, manifest);
8588
+ await saveManifest3(project.root, manifest);
8175
8589
  if (DEBUG4) {
8176
8590
  console.log(chalk13.dim(`[pre-gen] Cleaned ${cleaned} orphaned component(s) from manifest`));
8177
8591
  }
@@ -8184,7 +8598,7 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
8184
8598
  let uxRecommendations;
8185
8599
  const SPLIT_THRESHOLD = 4;
8186
8600
  const parseOpts = { sharedComponentsSummary, projectRoot };
8187
- const modCtx = { config: config2, componentManager: cm };
8601
+ const modCtx = { config: dsm.getConfig(), componentManager: cm };
8188
8602
  const multiPageHint = /\b(pages?|sections?)\s*[:]\s*\w/i.test(message) || (message.match(
8189
8603
  /\b(?:registration|about|catal|account|contact|pricing|dashboard|settings|login|sign.?up|blog|portfolio|features)\b/gi
8190
8604
  ) || []).length >= SPLIT_THRESHOLD;
@@ -8194,7 +8608,7 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
8194
8608
  requests = splitResult.requests;
8195
8609
  if (splitResult.plan && projectRoot) {
8196
8610
  savePlan(projectRoot, splitResult.plan);
8197
- await ensurePlanGroupLayouts(projectRoot, splitResult.plan);
8611
+ await ensurePlanGroupLayouts(projectRoot, splitResult.plan, storedHashes, dsm.getConfig());
8198
8612
  }
8199
8613
  uxRecommendations = void 0;
8200
8614
  } catch {
@@ -8233,8 +8647,24 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
8233
8647
  }
8234
8648
  }
8235
8649
  } else {
8650
+ let reusePlanDirective;
8651
+ try {
8652
+ const singlePageManifest = await loadManifest8(projectRoot);
8653
+ if (singlePageManifest.shared.length > 0) {
8654
+ const reusePlan = buildReusePlan({
8655
+ pageName: "page",
8656
+ pageType: inferPageTypeFromRoute("/"),
8657
+ sections: [],
8658
+ manifest: singlePageManifest,
8659
+ existingPageCode: {},
8660
+ userRequest: message
8661
+ });
8662
+ reusePlanDirective = buildReusePlanDirective(reusePlan) || void 0;
8663
+ }
8664
+ } catch {
8665
+ }
8236
8666
  try {
8237
- const result = await parseModification(message, modCtx, provider, parseOpts);
8667
+ const result = await parseModification(message, modCtx, provider, { ...parseOpts, reusePlanDirective });
8238
8668
  requests = result.requests;
8239
8669
  uxRecommendations = result.uxRecommendations;
8240
8670
  const pagesWithoutCode = requests.filter(
@@ -8272,7 +8702,7 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
8272
8702
  requests = splitResult.requests;
8273
8703
  if (splitResult.plan && projectRoot) {
8274
8704
  savePlan(projectRoot, splitResult.plan);
8275
- await ensurePlanGroupLayouts(projectRoot, splitResult.plan);
8705
+ await ensurePlanGroupLayouts(projectRoot, splitResult.plan, storedHashes, dsm.getConfig());
8276
8706
  }
8277
8707
  uxRecommendations = void 0;
8278
8708
  } catch {
@@ -8798,7 +9228,7 @@ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${compon
8798
9228
  }
8799
9229
  }
8800
9230
  if (manifestChanged) {
8801
- await saveManifest2(projectRoot, currentManifest);
9231
+ await saveManifest3(projectRoot, currentManifest);
8802
9232
  if (DEBUG4) console.log(chalk13.dim("[auto-sync] Manifest updated"));
8803
9233
  }
8804
9234
  } catch {
@@ -8969,7 +9399,7 @@ import { DesignSystemManager as DesignSystemManager8, ComponentGenerator as Comp
8969
9399
  // src/utils/file-watcher.ts
8970
9400
  import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, existsSync as existsSync19 } from "fs";
8971
9401
  import { relative as relative4, join as join13 } from "path";
8972
- import { loadManifest as loadManifest9, saveManifest as saveManifest3 } from "@getcoherent/core";
9402
+ import { loadManifest as loadManifest9, saveManifest as saveManifest4 } from "@getcoherent/core";
8973
9403
 
8974
9404
  // src/utils/component-integrity.ts
8975
9405
  import { existsSync as existsSync18, readFileSync as readFileSync13, readdirSync as readdirSync5 } from "fs";
@@ -9357,7 +9787,7 @@ async function handleFileDelete(projectRoot, filePath) {
9357
9787
  ...manifest,
9358
9788
  shared: manifest.shared.filter((s) => s.id !== orphaned.id)
9359
9789
  };
9360
- await saveManifest3(projectRoot, cleaned);
9790
+ await saveManifest4(projectRoot, cleaned);
9361
9791
  console.log(chalk33.cyan(`
9362
9792
  \u{1F5D1} Auto-removed ${orphaned.id} (${orphaned.name}) \u2014 file deleted`));
9363
9793
  await writeCursorRules(projectRoot);
@@ -9594,6 +10024,41 @@ async function fixMissingComponentExports(projectRoot) {
9594
10024
  }
9595
10025
  }
9596
10026
  }
10027
+ const neededSharedExports = /* @__PURE__ */ new Map();
10028
+ for (const file of pages) {
10029
+ const content = readFileSync15(file, "utf-8");
10030
+ const sharedImportRe = /import\s*\{([^}]+)\}\s*from\s*['"]@\/components\/shared\/([^'"]+)['"]/g;
10031
+ let sm;
10032
+ while ((sm = sharedImportRe.exec(content)) !== null) {
10033
+ const names = sm[1].split(",").map((s) => s.trim()).filter(Boolean);
10034
+ const componentId = sm[2];
10035
+ if (!neededSharedExports.has(componentId)) neededSharedExports.set(componentId, /* @__PURE__ */ new Set());
10036
+ for (const name of names) neededSharedExports.get(componentId).add(name);
10037
+ }
10038
+ }
10039
+ for (const [componentId, needed] of neededSharedExports) {
10040
+ const componentFile = join14(sharedDir, `${componentId}.tsx`);
10041
+ if (!existsSync20(componentFile)) continue;
10042
+ let content = readFileSync15(componentFile, "utf-8");
10043
+ const exportRe = /export\s+(?:const|function|class)\s+(\w+)|export\s*\{([^}]+)\}/g;
10044
+ const existingExports = /* @__PURE__ */ new Set();
10045
+ let em;
10046
+ while ((em = exportRe.exec(content)) !== null) {
10047
+ if (em[1]) existingExports.add(em[1]);
10048
+ if (em[2])
10049
+ em[2].split(",").map(
10050
+ (s) => s.trim().split(/\s+as\s+/).pop()
10051
+ ).filter(Boolean).forEach((n) => existingExports.add(n));
10052
+ }
10053
+ const missing = [...needed].filter((n) => !existingExports.has(n));
10054
+ if (missing.length === 0) continue;
10055
+ const defaultExportMatch = content.match(/export\s+default\s+function\s+(\w+)/);
10056
+ if (defaultExportMatch && missing.includes(defaultExportMatch[1])) {
10057
+ content = content.replace(/export\s+default\s+function\s+(\w+)/, "export function $1");
10058
+ writeFileSync10(componentFile, content, "utf-8");
10059
+ console.log(chalk14.dim(` \u2714 Fixed export in ${componentId}.tsx (default \u2192 named)`));
10060
+ }
10061
+ }
9597
10062
  }
9598
10063
  async function backfillPageAnalysis(projectRoot) {
9599
10064
  const configPath = join14(projectRoot, "design-system.config.ts");
@@ -10318,7 +10783,7 @@ import {
10318
10783
  PageManager as PageManager4,
10319
10784
  ComponentGenerator as ComponentGenerator4,
10320
10785
  loadManifest as loadManifest10,
10321
- saveManifest as saveManifest4
10786
+ saveManifest as saveManifest5
10322
10787
  } from "@getcoherent/core";
10323
10788
  function extractComponentIdsFromCode2(code) {
10324
10789
  const ids = /* @__PURE__ */ new Set();
@@ -10587,7 +11052,7 @@ async function fixCommand(opts = {}) {
10587
11052
  manifestModified = true;
10588
11053
  }
10589
11054
  if (manifestModified && !dryRun) {
10590
- await saveManifest4(project.root, manifest);
11055
+ await saveManifest5(project.root, manifest);
10591
11056
  fixes.push("Shared component manifest updated");
10592
11057
  }
10593
11058
  for (const entry of manifest.shared) {
@@ -11124,10 +11589,15 @@ function createComponentsCommand() {
11124
11589
  });
11125
11590
  console.log(chalk25.cyan("\u{1F4A1} Modify by ID:"), chalk25.white('coherent chat "in CID-001 add a search button"\n'));
11126
11591
  });
11127
- sharedCmd.command("add <name>").description("Create a shared component (layout/section/widget) and register in manifest").option("-t, --type <type>", "Type: layout | section | widget", "layout").option("-d, --description <desc>", "Description").action(async (name, opts) => {
11592
+ sharedCmd.command("add <name>").description("Create a shared component (layout/section/widget) and register in manifest").option(
11593
+ "-t, --type <type>",
11594
+ "Type: layout | navigation | data-display | form | feedback | section | widget",
11595
+ "layout"
11596
+ ).option("-d, --description <desc>", "Description").action(async (name, opts) => {
11128
11597
  const project = findConfig();
11129
11598
  if (!project) exitNotCoherent();
11130
- const type = opts.type === "section" || opts.type === "widget" ? opts.type : "layout";
11599
+ const validTypes = ["layout", "navigation", "data-display", "form", "feedback", "section", "widget"];
11600
+ const type = validTypes.includes(opts.type ?? "") ? opts.type : "layout";
11131
11601
  const result = await generateSharedComponent5(project.root, {
11132
11602
  name: name.trim(),
11133
11603
  type,
@@ -11803,7 +12273,7 @@ import { existsSync as existsSync27, readFileSync as readFileSync19 } from "fs";
11803
12273
  import { join as join20, relative as relative5, dirname as dirname10 } from "path";
11804
12274
  import { readdir as readdir4, readFile as readFile7 } from "fs/promises";
11805
12275
  import { DesignSystemManager as DesignSystemManager16 } from "@getcoherent/core";
11806
- import { loadManifest as loadManifest13, saveManifest as saveManifest5, findSharedComponent } from "@getcoherent/core";
12276
+ import { loadManifest as loadManifest13, saveManifest as saveManifest6, findSharedComponent } from "@getcoherent/core";
11807
12277
  function extractTokensFromProject(projectRoot) {
11808
12278
  const lightColors = {};
11809
12279
  const darkColors = {};
@@ -12099,7 +12569,7 @@ async function syncCommand(options = {}) {
12099
12569
  const { manifest: reconciledManifest, result: rr } = reconcileComponents(project.root, manifest);
12100
12570
  reconcileResult = rr;
12101
12571
  if (!dryRun) {
12102
- await saveManifest5(project.root, reconciledManifest);
12572
+ await saveManifest6(project.root, reconciledManifest);
12103
12573
  }
12104
12574
  detectedComponents = await detectCustomComponents(project.root, allPageCode);
12105
12575
  for (const comp of detectedComponents) {
@@ -365,7 +365,7 @@ Rules:
365
365
  - Do NOT use these names (already shared): ${existingSharedNames.join(", ")}
366
366
  - Look for: cards with icon+title+description, pricing tiers, testimonial blocks, stat displays, CTA sections
367
367
 
368
- Each component object: "name" (PascalCase), "type" ("section"|"widget"), "description", "propsInterface", "code" (full TSX module as string)
368
+ Each component object: "name" (PascalCase), "type" ("layout"|"navigation"|"data-display"|"form"|"feedback"|"section"|"widget"), "description", "propsInterface", "code" (full TSX module as string)
369
369
 
370
370
  If no repeating patterns found: { "components": [] }`
371
371
  }
@@ -11,7 +11,7 @@ import {
11
11
  routeToKey,
12
12
  savePlan,
13
13
  updateArchitecturePlan
14
- } from "./chunk-WRDWFCQJ.js";
14
+ } from "./chunk-PVJJ2YXP.js";
15
15
  import "./chunk-5AHG4NNX.js";
16
16
  import "./chunk-3RG5ZIWI.js";
17
17
  export {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.6.14",
6
+ "version": "0.6.16",
7
7
  "description": "CLI interface for Coherent Design Method",
8
8
  "type": "module",
9
9
  "main": "./dist/index.js",
@@ -33,8 +33,15 @@
33
33
  ],
34
34
  "author": "Coherent Design Method",
35
35
  "license": "MIT",
36
+ "scripts": {
37
+ "dev": "tsup --watch",
38
+ "build": "tsup",
39
+ "typecheck": "tsc --noEmit",
40
+ "test": "vitest"
41
+ },
36
42
  "dependencies": {
37
43
  "@anthropic-ai/sdk": "^0.32.0",
44
+ "@getcoherent/core": "workspace:*",
38
45
  "chalk": "^5.3.0",
39
46
  "chokidar": "^4.0.1",
40
47
  "commander": "^11.1.0",
@@ -42,19 +49,12 @@
42
49
  "open": "^10.1.0",
43
50
  "ora": "^7.0.1",
44
51
  "prompts": "^2.4.2",
45
- "zod": "^3.22.4",
46
- "@getcoherent/core": "0.6.14"
52
+ "zod": "^3.22.4"
47
53
  },
48
54
  "devDependencies": {
49
55
  "@types/node": "^20.11.0",
50
56
  "@types/prompts": "^2.4.9",
51
57
  "tsup": "^8.0.1",
52
58
  "typescript": "^5.3.3"
53
- },
54
- "scripts": {
55
- "dev": "tsup --watch",
56
- "build": "tsup",
57
- "typecheck": "tsc --noEmit",
58
- "test": "vitest"
59
59
  }
60
- }
60
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Sergei Kovtun
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.