@getcoherent/cli 0.2.3 → 0.3.0

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 (2) hide show
  1. package/dist/index.js +371 -504
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -601,7 +601,7 @@ function installPackages(projectRoot, packages) {
601
601
  if (packages.length === 0) return Promise.resolve(true);
602
602
  const safe = packages.filter((p) => SAFE_PKG_NAME.test(p));
603
603
  if (safe.length === 0) return Promise.resolve(true);
604
- return new Promise((resolve17) => {
604
+ return new Promise((resolve16) => {
605
605
  try {
606
606
  const hasPnpm = existsSync4(join2(projectRoot, "pnpm-lock.yaml"));
607
607
  if (hasPnpm) {
@@ -612,10 +612,10 @@ function installPackages(projectRoot, packages) {
612
612
  stdio: "pipe"
613
613
  });
614
614
  }
615
- resolve17(true);
615
+ resolve16(true);
616
616
  } catch (e) {
617
617
  if (process.env.COHERENT_DEBUG === "1") console.error("Failed to install packages:", e);
618
- resolve17(false);
618
+ resolve16(false);
619
619
  }
620
620
  });
621
621
  }
@@ -1974,7 +1974,7 @@ Run in terminal:
1974
1974
  ## Platform Overlay (DO NOT TOUCH)
1975
1975
 
1976
1976
  The following are dev-only platform features, excluded from production export:
1977
- - Floating Design System button (in AppNav.tsx)
1977
+ - Floating Design System button (in shared Header component)
1978
1978
  - /design-system/* pages
1979
1979
  - /api/design-system/* routes
1980
1980
  - coherent.components.json
@@ -2425,8 +2425,8 @@ export default config
2425
2425
  // src/commands/chat.ts
2426
2426
  import chalk13 from "chalk";
2427
2427
  import ora2 from "ora";
2428
- import { resolve as resolve10 } from "path";
2429
- import { existsSync as existsSync16, readFileSync as readFileSync11, mkdirSync as mkdirSync6 } from "fs";
2428
+ import { resolve as resolve9 } from "path";
2429
+ import { existsSync as existsSync16, readFileSync as readFileSync10, mkdirSync as mkdirSync6 } from "fs";
2430
2430
  import {
2431
2431
  DesignSystemManager as DesignSystemManager7,
2432
2432
  ComponentManager as ComponentManager4,
@@ -4480,6 +4480,14 @@ Format:
4480
4480
  "pageCode": "import { Metadata } from 'next'\\n..."
4481
4481
  }
4482
4482
 
4483
+ LAYOUT CONTRACT (CRITICAL \u2014 prevents duplicate navigation and footer):
4484
+ - The app has a root layout (app/layout.tsx) that renders a shared Header and Footer.
4485
+ - Pages are rendered INSIDE this layout, between the Header and Footer.
4486
+ - NEVER include <header>, <nav>, or <footer> elements in pageCode.
4487
+ - Start page content with <main> or a wrapper <div>. The first visible element should be the page title or hero section.
4488
+ - If the page needs sub-navigation (tabs, breadcrumbs, sidebar nav), use elements like <div role="tablist"> or <aside> \u2014 NOT <header>, <nav>, or <footer>.
4489
+ - Do NOT add any navigation bars, logo headers, site-wide menus, or site footers to pages. The layout provides all of these.
4490
+
4483
4491
  pageCode rules (shadcn/ui blocks quality):
4484
4492
  - Full Next.js App Router page. Imports from '@/components/ui/...' for registry components.
4485
4493
  - Follow ALL design constraints above: text-sm base, semantic colors only, restricted spacing, weight-based hierarchy.
@@ -5849,14 +5857,14 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
5849
5857
  import { resolve as resolve7 } from "path";
5850
5858
  import { mkdir as mkdir3 } from "fs/promises";
5851
5859
  import { dirname as dirname6 } from "path";
5852
- import chalk10 from "chalk";
5860
+ import chalk11 from "chalk";
5853
5861
  import {
5854
5862
  getTemplateForPageType,
5855
- loadManifest as loadManifest5,
5863
+ loadManifest as loadManifest6,
5856
5864
  saveManifest,
5857
5865
  updateUsedIn,
5858
5866
  findSharedComponentByIdOrName,
5859
- generateSharedComponent as generateSharedComponent2
5867
+ generateSharedComponent as generateSharedComponent3
5860
5868
  } from "@getcoherent/core";
5861
5869
 
5862
5870
  // src/utils/quality-validator.ts
@@ -6530,36 +6538,41 @@ ${fixed}`;
6530
6538
  } catch {
6531
6539
  }
6532
6540
  if (lucideExports2) {
6533
- const importedNames = new Set(
6541
+ const allImportedNames = /* @__PURE__ */ new Set();
6542
+ for (const m of fixed.matchAll(/import\s*\{([^}]+)\}\s*from/g)) {
6543
+ m[1].split(",").map((s) => s.trim()).filter(Boolean).forEach((n) => allImportedNames.add(n));
6544
+ }
6545
+ for (const m of fixed.matchAll(/import\s+([A-Z]\w+)\s+from/g)) {
6546
+ allImportedNames.add(m[1]);
6547
+ }
6548
+ const lucideImported = new Set(
6534
6549
  lucideImportMatch2[1].split(",").map((s) => s.trim()).filter(Boolean)
6535
6550
  );
6536
- const jsxIconRefs = [...fixed.matchAll(/<([A-Z][a-zA-Z]*(?:Icon)?)\s/g)].map((m) => m[1]);
6537
- const htmlElements = /* @__PURE__ */ new Set(["Link", "Fragment", "Suspense", "Image"]);
6551
+ const jsxIconRefs = [...new Set([...fixed.matchAll(/<([A-Z][a-zA-Z]*Icon)\s/g)].map((m) => m[1]))];
6538
6552
  const missing = [];
6539
6553
  for (const ref of jsxIconRefs) {
6540
- if (importedNames.has(ref) || htmlElements.has(ref)) continue;
6554
+ if (allImportedNames.has(ref)) continue;
6541
6555
  if (fixed.includes(`function ${ref}`) || fixed.includes(`const ${ref}`)) continue;
6542
- if (/^import\s.*\b${ref}\b/m.test(fixed)) continue;
6543
6556
  const baseName = ref.replace(/Icon$/, "");
6544
6557
  if (lucideExports2.has(ref)) {
6545
6558
  missing.push(ref);
6546
- importedNames.add(ref);
6559
+ lucideImported.add(ref);
6547
6560
  } else if (lucideExports2.has(baseName)) {
6548
6561
  const re = new RegExp(`\\b${ref}\\b`, "g");
6549
6562
  fixed = fixed.replace(re, baseName);
6550
6563
  missing.push(baseName);
6551
- importedNames.add(baseName);
6564
+ lucideImported.add(baseName);
6552
6565
  fixes.push(`renamed ${ref} \u2192 ${baseName} (lucide-react)`);
6553
6566
  } else {
6554
6567
  const fallback = "Circle";
6555
6568
  const re = new RegExp(`\\b${ref}\\b`, "g");
6556
6569
  fixed = fixed.replace(re, fallback);
6557
- importedNames.add(fallback);
6570
+ lucideImported.add(fallback);
6558
6571
  fixes.push(`unknown icon ${ref} \u2192 ${fallback}`);
6559
6572
  }
6560
6573
  }
6561
6574
  if (missing.length > 0) {
6562
- const allNames = [...importedNames];
6575
+ const allNames = [...lucideImported];
6563
6576
  const origLine = lucideImportMatch2[0];
6564
6577
  fixed = fixed.replace(origLine, `import { ${allNames.join(", ")} } from "lucide-react"`);
6565
6578
  fixes.push(`added missing lucide imports: ${missing.join(", ")}`);
@@ -6609,7 +6622,8 @@ import {
6609
6622
  PageGenerator,
6610
6623
  TailwindConfigGenerator
6611
6624
  } from "@getcoherent/core";
6612
- import { integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout2 } from "@getcoherent/core";
6625
+ import { integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout2, loadManifest as loadManifest5, generateSharedComponent as generateSharedComponent2 } from "@getcoherent/core";
6626
+ import chalk9 from "chalk";
6613
6627
  async function validateAndFixGeneratedCode(projectRoot, code, options = {}) {
6614
6628
  const fixes = [];
6615
6629
  let fixed = fixEscapedClosingQuotes(code);
@@ -6679,18 +6693,49 @@ async function regenerateLayout(config2, projectRoot) {
6679
6693
  const layout = config2.pages[0]?.layout || "centered";
6680
6694
  const appType = config2.settings.appType || "multi-page";
6681
6695
  const generator = new PageGenerator(config2);
6682
- const code = await generator.generateLayout(layout, appType);
6683
- const layoutPath = resolve6(projectRoot, "app", "layout.tsx");
6684
- await writeFile(layoutPath, code);
6685
- if (config2.navigation?.enabled && appType === "multi-page") {
6686
- const appNavCode = generator.generateAppNav();
6687
- const appNavPath = resolve6(projectRoot, "app", "AppNav.tsx");
6688
- await writeFile(appNavPath, appNavCode);
6696
+ let manifest = null;
6697
+ try {
6698
+ manifest = await loadManifest5(projectRoot);
6699
+ } catch {
6700
+ }
6701
+ const hasSharedHeader = manifest?.shared.some((c) => c.type === "layout" && /header|nav/i.test(c.name)) ?? false;
6702
+ const hasSharedFooter = manifest?.shared.some((c) => c.type === "layout" && /footer/i.test(c.name)) ?? false;
6703
+ if (hasSharedHeader) {
6704
+ const code = await generator.generateLayout(layout, appType, { skipNav: true });
6705
+ const layoutPath = resolve6(projectRoot, "app", "layout.tsx");
6706
+ await writeFile(layoutPath, code);
6707
+ } else {
6708
+ const code = await generator.generateLayout(layout, appType, { skipNav: true });
6709
+ const layoutPath = resolve6(projectRoot, "app", "layout.tsx");
6710
+ await writeFile(layoutPath, code);
6711
+ if (config2.navigation?.enabled && appType === "multi-page") {
6712
+ const headerCode = generator.generateSharedHeaderCode();
6713
+ await generateSharedComponent2(projectRoot, {
6714
+ name: "Header",
6715
+ type: "layout",
6716
+ code: headerCode,
6717
+ description: "Main site header with navigation and theme toggle",
6718
+ usedIn: ["app/layout.tsx"]
6719
+ });
6720
+ if (!hasSharedFooter) {
6721
+ const footerCode = generator.generateSharedFooterCode();
6722
+ await generateSharedComponent2(projectRoot, {
6723
+ name: "Footer",
6724
+ type: "layout",
6725
+ code: footerCode,
6726
+ description: "Site footer",
6727
+ usedIn: ["app/layout.tsx"]
6728
+ });
6729
+ }
6730
+ }
6689
6731
  }
6690
6732
  try {
6691
6733
  await integrateSharedLayoutIntoRootLayout2(projectRoot);
6692
6734
  await ensureAuthRouteGroup(projectRoot);
6693
- } catch {
6735
+ } catch (err) {
6736
+ if (process.env.COHERENT_DEBUG === "1") {
6737
+ console.log(chalk9.dim("Layout integration warning:", err));
6738
+ }
6694
6739
  }
6695
6740
  }
6696
6741
  async function regenerateFiles(modified, config2, projectRoot) {
@@ -6728,8 +6773,39 @@ async function regenerateFiles(modified, config2, projectRoot) {
6728
6773
  }
6729
6774
  }
6730
6775
 
6776
+ // src/commands/chat/jsx-extractor.ts
6777
+ function extractBalancedTag(source, tagName) {
6778
+ const openRe = new RegExp(`<${tagName}\\b`, "gi");
6779
+ const match = openRe.exec(source);
6780
+ if (!match) return null;
6781
+ const startIdx = match.index;
6782
+ const openTagRe = new RegExp(`<${tagName}\\b`, "gi");
6783
+ const closeTagRe = new RegExp(`</${tagName}>`, "gi");
6784
+ const events = [];
6785
+ let m;
6786
+ openTagRe.lastIndex = startIdx;
6787
+ while ((m = openTagRe.exec(source)) !== null) {
6788
+ events.push({ pos: m.index, type: "open", end: m.index + m[0].length });
6789
+ }
6790
+ closeTagRe.lastIndex = startIdx;
6791
+ while ((m = closeTagRe.exec(source)) !== null) {
6792
+ events.push({ pos: m.index, type: "close", end: m.index + m[0].length });
6793
+ }
6794
+ events.sort((a, b) => a.pos - b.pos);
6795
+ let depth = 0;
6796
+ for (const ev of events) {
6797
+ if (ev.pos < startIdx) continue;
6798
+ if (ev.type === "open") depth++;
6799
+ else {
6800
+ depth--;
6801
+ if (depth === 0) return source.slice(startIdx, ev.end);
6802
+ }
6803
+ }
6804
+ return null;
6805
+ }
6806
+
6731
6807
  // src/commands/chat/reporting.ts
6732
- import chalk9 from "chalk";
6808
+ import chalk10 from "chalk";
6733
6809
  function extractImportsFrom(code, fromPath) {
6734
6810
  const escaped = fromPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6735
6811
  const regex = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*['"\`]${escaped}[^'"\`]*['"\`]`, "g");
@@ -6751,27 +6827,27 @@ function printPostGenerationReport(opts) {
6751
6827
  const iconCount = extractImportsFrom(code, "lucide-react").length;
6752
6828
  const hasInstalled = postFixes.some((f) => f.startsWith("Installed:"));
6753
6829
  const syntaxStatus = postFixes.length > 0 ? postFixes.some((f) => f.includes("metadata")) ? "fixed (escaped metadata quotes) \u2714" : "fixed \u2714" : "valid \u2714";
6754
- console.log(chalk9.green(`
6830
+ console.log(chalk10.green(`
6755
6831
  \u2705 Page "${pageTitle}" ${action} at ${filePath}
6756
6832
  `));
6757
6833
  if (uiComponents.length > 0) {
6758
- console.log(chalk9.dim(` Components: ${uiComponents.join(", ")} (from @/components/ui)`));
6834
+ console.log(chalk10.dim(` Components: ${uiComponents.join(", ")} (from @/components/ui)`));
6759
6835
  }
6760
6836
  if (inCodeShared.length > 0) {
6761
- console.log(chalk9.dim(` Shared: ${inCodeShared.map((s) => `${s.id} (${s.name})`).join(", ")}`));
6837
+ console.log(chalk10.dim(` Shared: ${inCodeShared.map((s) => `${s.id} (${s.name})`).join(", ")}`));
6762
6838
  }
6763
6839
  if (layoutShared.length > 0) {
6764
- console.log(chalk9.dim(` Layout: ${layoutShared.map((l) => `${l.id} (${l.name})`).join(", ")} via layout.tsx`));
6840
+ console.log(chalk10.dim(` Layout: ${layoutShared.map((l) => `${l.id} (${l.name})`).join(", ")} via layout.tsx`));
6765
6841
  }
6766
6842
  if (iconCount > 0) {
6767
- console.log(chalk9.dim(` Icons: ${iconCount} from lucide-react`));
6843
+ console.log(chalk10.dim(` Icons: ${iconCount} from lucide-react`));
6768
6844
  }
6769
6845
  if (hasInstalled) {
6770
- console.log(chalk9.dim(" Dependencies: installed \u2714"));
6846
+ console.log(chalk10.dim(" Dependencies: installed \u2714"));
6771
6847
  }
6772
- console.log(chalk9.dim(` Syntax: ${syntaxStatus}`));
6848
+ console.log(chalk10.dim(` Syntax: ${syntaxStatus}`));
6773
6849
  if (route) {
6774
- console.log(chalk9.cyan(`
6850
+ console.log(chalk10.cyan(`
6775
6851
  Preview: http://localhost:3000${route}`));
6776
6852
  }
6777
6853
  console.log("");
@@ -6779,35 +6855,35 @@ function printPostGenerationReport(opts) {
6779
6855
  function printSharedComponentReport(opts) {
6780
6856
  const { id, name, file, instruction, postFixes = [] } = opts;
6781
6857
  const syntaxStatus = postFixes.length > 0 ? "fixed \u2714" : "valid \u2714";
6782
- console.log(chalk9.green(`
6858
+ console.log(chalk10.green(`
6783
6859
  \u2705 Updated ${id} (${name}) at ${file}
6784
6860
  `));
6785
6861
  if (instruction) {
6786
6862
  const snippet = instruction.length > 60 ? instruction.slice(0, 57) + "..." : instruction;
6787
- console.log(chalk9.dim(` Changed: ${snippet}`));
6863
+ console.log(chalk10.dim(` Changed: ${snippet}`));
6788
6864
  }
6789
- console.log(chalk9.dim(" Affects: all pages via layout.tsx"));
6790
- console.log(chalk9.dim(` Syntax: ${syntaxStatus}`));
6865
+ console.log(chalk10.dim(" Affects: all pages via layout.tsx"));
6866
+ console.log(chalk10.dim(` Syntax: ${syntaxStatus}`));
6791
6867
  console.log("");
6792
6868
  }
6793
6869
  function printLinkSharedReport(opts) {
6794
6870
  const { sharedId, sharedName, pageTarget, route, postFixes = [] } = opts;
6795
6871
  const syntaxStatus = postFixes.length > 0 ? "fixed \u2714" : "valid \u2714";
6796
- console.log(chalk9.green(`
6872
+ console.log(chalk10.green(`
6797
6873
  \u2705 Linked ${sharedId} (${sharedName}) to page "${pageTarget}"
6798
6874
  `));
6799
- console.log(chalk9.dim(` Syntax: ${syntaxStatus}`));
6800
- console.log(chalk9.cyan(` Preview: http://localhost:3000${route}`));
6875
+ console.log(chalk10.dim(` Syntax: ${syntaxStatus}`));
6876
+ console.log(chalk10.cyan(` Preview: http://localhost:3000${route}`));
6801
6877
  console.log("");
6802
6878
  }
6803
6879
  function printPromoteAndLinkReport(opts) {
6804
6880
  const { id, name, file, usedInFiles, postFixes = [] } = opts;
6805
6881
  const syntaxStatus = postFixes.length > 0 ? "fixed \u2714" : "valid \u2714";
6806
- console.log(chalk9.green(`
6882
+ console.log(chalk10.green(`
6807
6883
  \u2705 Created ${id} (${name}) at ${file}
6808
6884
  `));
6809
- console.log(chalk9.dim(` Linked to: ${usedInFiles.length} page(s)`));
6810
- console.log(chalk9.dim(` Syntax: ${syntaxStatus}`));
6885
+ console.log(chalk10.dim(` Linked to: ${usedInFiles.length} page(s)`));
6886
+ console.log(chalk10.dim(` Syntax: ${syntaxStatus}`));
6811
6887
  console.log("");
6812
6888
  }
6813
6889
  function showPreview(requests, results, config2, preflightInstalledNames) {
@@ -6825,23 +6901,23 @@ function showPreview(requests, results, config2, preflightInstalledNames) {
6825
6901
  const modifiedSharedComponents = successfulPairs.filter(({ request }) => request.type === "modify-layout-block");
6826
6902
  const modifiedPages = successfulPairs.filter(({ request }) => request.type === "update-page");
6827
6903
  const tokenChanges = successfulPairs.filter(({ request }) => request.type === "update-token");
6828
- console.log(chalk9.bold.cyan("\n\u{1F4CB} Changes Applied:\n"));
6904
+ console.log(chalk10.bold.cyan("\n\u{1F4CB} Changes Applied:\n"));
6829
6905
  if (preflightInstalledNames && preflightInstalledNames.length > 0) {
6830
- console.log(chalk9.cyan("\u{1F50D} Pre-flight check: Installed missing components:"));
6906
+ console.log(chalk10.cyan("\u{1F50D} Pre-flight check: Installed missing components:"));
6831
6907
  preflightInstalledNames.forEach((name) => {
6832
- console.log(chalk9.green(` \u2728 Auto-installed ${name}`));
6908
+ console.log(chalk10.green(` \u2728 Auto-installed ${name}`));
6833
6909
  });
6834
6910
  console.log("");
6835
6911
  }
6836
6912
  if (addedComponents.length > 0) {
6837
6913
  const names = addedComponents.map(({ request }) => request.changes.name).filter(Boolean);
6838
- console.log(chalk9.green("\u{1F4E6} Components:"));
6839
- console.log(chalk9.white(` \u2728 Auto-installed: ${names.join(", ")}`));
6914
+ console.log(chalk10.green("\u{1F4E6} Components:"));
6915
+ console.log(chalk10.white(` \u2728 Auto-installed: ${names.join(", ")}`));
6840
6916
  }
6841
6917
  if (customComponents.length > 0) {
6842
6918
  const names = customComponents.map(({ request }) => request.changes.name).filter(Boolean);
6843
- if (addedComponents.length === 0) console.log(chalk9.green("\u{1F4E6} Components:"));
6844
- console.log(chalk9.white(` \u2728 Created: ${names.join(", ")}`));
6919
+ if (addedComponents.length === 0) console.log(chalk10.green("\u{1F4E6} Components:"));
6920
+ console.log(chalk10.white(` \u2728 Created: ${names.join(", ")}`));
6845
6921
  }
6846
6922
  const usedComponentIds = /* @__PURE__ */ new Set();
6847
6923
  addedPages.forEach(({ request }) => {
@@ -6856,71 +6932,71 @@ function showPreview(requests, results, config2, preflightInstalledNames) {
6856
6932
  ]);
6857
6933
  const reusedIds = [...usedComponentIds].filter((id) => !newComponentIds.has(id));
6858
6934
  if (reusedIds.length > 0) {
6859
- if (addedComponents.length === 0 && customComponents.length === 0) console.log(chalk9.green("\u{1F4E6} Components:"));
6860
- console.log(chalk9.white(` \u{1F504} Reused: ${reusedIds.join(", ")}`));
6935
+ if (addedComponents.length === 0 && customComponents.length === 0) console.log(chalk10.green("\u{1F4E6} Components:"));
6936
+ console.log(chalk10.white(` \u{1F504} Reused: ${reusedIds.join(", ")}`));
6861
6937
  }
6862
6938
  if (addedComponents.length > 0 || customComponents.length > 0 || reusedIds.length > 0) {
6863
6939
  console.log("");
6864
6940
  }
6865
6941
  if (addedPages.length > 0) {
6866
- console.log(chalk9.green("\u{1F4C4} Pages Created:"));
6942
+ console.log(chalk10.green("\u{1F4C4} Pages Created:"));
6867
6943
  addedPages.forEach(({ request }) => {
6868
6944
  const page = request.changes;
6869
6945
  const route = page.route || "/";
6870
- console.log(chalk9.white(` \u2728 ${page.name || "Page"}`));
6871
- console.log(chalk9.gray(` Route: ${route}`));
6872
- console.log(chalk9.gray(` Sections: ${page.sections?.length ?? 0}`));
6946
+ console.log(chalk10.white(` \u2728 ${page.name || "Page"}`));
6947
+ console.log(chalk10.gray(` Route: ${route}`));
6948
+ console.log(chalk10.gray(` Sections: ${page.sections?.length ?? 0}`));
6873
6949
  });
6874
6950
  console.log("");
6875
6951
  }
6876
6952
  if (modifiedComponents.length > 0 || modifiedSharedComponents.length > 0 || modifiedPages.length > 0 || tokenChanges.length > 0) {
6877
- console.log(chalk9.yellow("\u{1F527} Modified:"));
6953
+ console.log(chalk10.yellow("\u{1F527} Modified:"));
6878
6954
  modifiedComponents.forEach(({ result }) => {
6879
- console.log(chalk9.white(` \u2022 ${result.message}`));
6955
+ console.log(chalk10.white(` \u2022 ${result.message}`));
6880
6956
  });
6881
6957
  modifiedSharedComponents.forEach(({ result }) => {
6882
- console.log(chalk9.white(` \u2022 ${result.message}`));
6958
+ console.log(chalk10.white(` \u2022 ${result.message}`));
6883
6959
  });
6884
6960
  modifiedPages.forEach(({ result }) => {
6885
- console.log(chalk9.white(` \u2022 ${result.message}`));
6961
+ console.log(chalk10.white(` \u2022 ${result.message}`));
6886
6962
  });
6887
6963
  tokenChanges.forEach(({ result }) => {
6888
- console.log(chalk9.white(` \u2022 ${result.message}`));
6964
+ console.log(chalk10.white(` \u2022 ${result.message}`));
6889
6965
  });
6890
6966
  console.log("");
6891
6967
  }
6892
6968
  if (failedPairs.length > 0) {
6893
- console.log(chalk9.red("\u274C Failed modifications:"));
6969
+ console.log(chalk10.red("\u274C Failed modifications:"));
6894
6970
  failedPairs.forEach(({ result }) => {
6895
- console.log(chalk9.gray(` \u2716 ${result.message}`));
6971
+ console.log(chalk10.gray(` \u2716 ${result.message}`));
6896
6972
  });
6897
6973
  console.log("");
6898
6974
  }
6899
6975
  const successCount = successfulPairs.length;
6900
6976
  const totalCount = results.length;
6901
6977
  if (successCount === totalCount) {
6902
- console.log(chalk9.green.bold(`\u2705 Success! ${successCount} modification(s) applied
6978
+ console.log(chalk10.green.bold(`\u2705 Success! ${successCount} modification(s) applied
6903
6979
  `));
6904
6980
  } else {
6905
- console.log(chalk9.yellow.bold(`\u26A0\uFE0F Partial success: ${successCount}/${totalCount} modification(s) applied
6981
+ console.log(chalk10.yellow.bold(`\u26A0\uFE0F Partial success: ${successCount}/${totalCount} modification(s) applied
6906
6982
  `));
6907
6983
  }
6908
6984
  if (addedPages.length > 0) {
6909
6985
  const firstPage = addedPages[0].request.changes;
6910
6986
  const route = firstPage?.route || "/";
6911
- console.log(chalk9.cyan("\u{1F680} What's next:\n"));
6912
- console.log(chalk9.white(" \u{1F4FA} View in browser:"));
6913
- console.log(chalk9.cyan(" coherent preview"));
6914
- console.log(chalk9.gray(` \u2192 Opens http://localhost:3000${route}
6987
+ console.log(chalk10.cyan("\u{1F680} What's next:\n"));
6988
+ console.log(chalk10.white(" \u{1F4FA} View in browser:"));
6989
+ console.log(chalk10.cyan(" coherent preview"));
6990
+ console.log(chalk10.gray(` \u2192 Opens http://localhost:3000${route}
6915
6991
  `));
6916
- console.log(chalk9.white(" \u{1F3A8} Customize:"));
6917
- console.log(chalk9.cyan(' coherent chat "make buttons rounded"'));
6918
- console.log(chalk9.cyan(` coherent chat "add hero section to ${firstPage?.name ?? "page"}"`));
6992
+ console.log(chalk10.white(" \u{1F3A8} Customize:"));
6993
+ console.log(chalk10.cyan(' coherent chat "make buttons rounded"'));
6994
+ console.log(chalk10.cyan(` coherent chat "add hero section to ${firstPage?.name ?? "page"}"`));
6919
6995
  console.log("");
6920
6996
  } else if (successCount > 0) {
6921
- console.log(chalk9.cyan("\u{1F680} What's next:\n"));
6922
- console.log(chalk9.white(" \u{1F4FA} Preview changes:"));
6923
- console.log(chalk9.cyan(" coherent preview\n"));
6997
+ console.log(chalk10.cyan("\u{1F680} What's next:\n"));
6998
+ console.log(chalk10.white(" \u{1F4FA} Preview changes:"));
6999
+ console.log(chalk10.cyan(" coherent preview\n"));
6924
7000
  }
6925
7001
  }
6926
7002
  function getChangeDescription(request, config2) {
@@ -6962,6 +7038,24 @@ function getChangeDescription(request, config2) {
6962
7038
 
6963
7039
  // src/commands/chat/modification-handler.ts
6964
7040
  var DEBUG2 = process.env.COHERENT_DEBUG === "1";
7041
+ function stripInlineLayoutElements(code) {
7042
+ let result = code;
7043
+ const stripped = [];
7044
+ const headerBlock = extractBalancedTag(result, "header");
7045
+ if (headerBlock) {
7046
+ result = result.replace(headerBlock, "");
7047
+ stripped.push("header");
7048
+ }
7049
+ const footerBlock = extractBalancedTag(result, "footer");
7050
+ if (footerBlock) {
7051
+ result = result.replace(footerBlock, "");
7052
+ stripped.push("footer");
7053
+ }
7054
+ if (stripped.length > 0) {
7055
+ result = result.replace(/\n{3,}/g, "\n\n");
7056
+ }
7057
+ return { code: result, stripped };
7058
+ }
6965
7059
  async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider, originalMessage) {
6966
7060
  switch (request.type) {
6967
7061
  case "modify-layout-block": {
@@ -7000,8 +7094,8 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7000
7094
  const newCode = await ai.editSharedComponentCode(currentCode, instruction, resolved.name);
7001
7095
  const { fixedCode, fixes } = await validateAndFixGeneratedCode(projectRoot, newCode, { isPage: false });
7002
7096
  if (fixes.length > 0) {
7003
- console.log(chalk10.dim(" \u{1F527} Post-generation fixes:"));
7004
- fixes.forEach((f) => console.log(chalk10.dim(` ${f}`)));
7097
+ console.log(chalk11.dim(" \u{1F527} Post-generation fixes:"));
7098
+ fixes.forEach((f) => console.log(chalk11.dim(` ${f}`)));
7005
7099
  }
7006
7100
  await writeFile(fullPath, fixedCode);
7007
7101
  printSharedComponentReport({
@@ -7074,11 +7168,11 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7074
7168
  const newPageCode = await ai.replaceInlineWithShared(pageCode, sharedCode, resolved.name, changes?.blockHint);
7075
7169
  const { fixedCode, fixes } = await validateAndFixGeneratedCode(projectRoot, newPageCode, { isPage: true });
7076
7170
  if (fixes.length > 0) {
7077
- console.log(chalk10.dim(" \u{1F527} Post-generation fixes:"));
7078
- fixes.forEach((f) => console.log(chalk10.dim(` ${f}`)));
7171
+ console.log(chalk11.dim(" \u{1F527} Post-generation fixes:"));
7172
+ fixes.forEach((f) => console.log(chalk11.dim(` ${f}`)));
7079
7173
  }
7080
7174
  await writeFile(pageFilePath, fixedCode);
7081
- const manifest = await loadManifest5(projectRoot);
7175
+ const manifest = await loadManifest6(projectRoot);
7082
7176
  const usedIn = manifest.shared.find((e) => e.id === resolved.id)?.usedIn ?? [];
7083
7177
  const routePath = route.replace(/^\//, "");
7084
7178
  const filePathRel = routePath ? `app/${routePath}/page.tsx` : "app/page.tsx";
@@ -7148,7 +7242,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7148
7242
  };
7149
7243
  }
7150
7244
  const extractedCode = await ai.extractBlockAsComponent(sourceCode, blockHint, componentName);
7151
- const created = await generateSharedComponent2(projectRoot, {
7245
+ const created = await generateSharedComponent3(projectRoot, {
7152
7246
  name: componentName,
7153
7247
  type: "section",
7154
7248
  code: extractedCode,
@@ -7177,13 +7271,13 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7177
7271
  const newCode = await ai.replaceInlineWithShared(linkPageCode, sharedCode, created.name, blockHint);
7178
7272
  const { fixedCode, fixes } = await validateAndFixGeneratedCode(projectRoot, newCode, { isPage: true });
7179
7273
  if (fixes.length > 0) {
7180
- console.log(chalk10.dim(" \u{1F527} Post-generation fixes:"));
7181
- fixes.forEach((f) => console.log(chalk10.dim(` ${f}`)));
7274
+ console.log(chalk11.dim(" \u{1F527} Post-generation fixes:"));
7275
+ fixes.forEach((f) => console.log(chalk11.dim(` ${f}`)));
7182
7276
  }
7183
7277
  await writeFile(fullPath, fixedCode);
7184
7278
  usedInFiles.push(relPath);
7185
7279
  }
7186
- const manifest = await loadManifest5(projectRoot);
7280
+ const manifest = await loadManifest6(projectRoot);
7187
7281
  const nextManifest = updateUsedIn(manifest, created.id, usedInFiles);
7188
7282
  await saveManifest(projectRoot, nextManifest);
7189
7283
  printPromoteAndLinkReport({
@@ -7272,7 +7366,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7272
7366
  const aiPageCode = typeof page.pageCode === "string" && page.pageCode.trim() !== "" ? page.pageCode : void 0;
7273
7367
  if (aiPageCode) {
7274
7368
  finalPageCode = aiPageCode;
7275
- if (DEBUG2) console.log(chalk10.dim(` [pageCode] Using AI-generated pageCode (user content priority)`));
7369
+ if (DEBUG2) console.log(chalk11.dim(` [pageCode] Using AI-generated pageCode (user content priority)`));
7276
7370
  } else if (page.pageType && page.structuredContent) {
7277
7371
  const templateFn = getTemplateForPageType(page.pageType);
7278
7372
  if (templateFn) {
@@ -7283,9 +7377,9 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7283
7377
  pageName
7284
7378
  };
7285
7379
  finalPageCode = templateFn(page.structuredContent, opts);
7286
- if (DEBUG2) console.log(chalk10.dim(` [template] Used "${page.pageType}" template (no pageCode provided)`));
7380
+ if (DEBUG2) console.log(chalk11.dim(` [template] Used "${page.pageType}" template (no pageCode provided)`));
7287
7381
  } catch {
7288
- if (DEBUG2) console.log(chalk10.dim(` [template] Failed for "${page.pageType}"`));
7382
+ if (DEBUG2) console.log(chalk11.dim(` [template] Failed for "${page.pageType}"`));
7289
7383
  }
7290
7384
  }
7291
7385
  }
@@ -7328,10 +7422,13 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7328
7422
  let codeToWrite = fixedCode;
7329
7423
  const { code: autoFixed, fixes: autoFixes } = await autoFixCode(codeToWrite);
7330
7424
  codeToWrite = autoFixed;
7425
+ const { code: layoutStripped, stripped } = stripInlineLayoutElements(codeToWrite);
7426
+ codeToWrite = layoutStripped;
7331
7427
  const allFixes = [...postFixes, ...autoFixes];
7428
+ if (stripped.length > 0) allFixes.push(`stripped inline ${stripped.join(", ")} (layout owns these)`);
7332
7429
  if (allFixes.length > 0) {
7333
- console.log(chalk10.dim(" \u{1F527} Post-generation fixes:"));
7334
- allFixes.forEach((f) => console.log(chalk10.dim(` ${f}`)));
7430
+ console.log(chalk11.dim(" \u{1F527} Post-generation fixes:"));
7431
+ allFixes.forEach((f) => console.log(chalk11.dim(` ${f}`)));
7335
7432
  }
7336
7433
  await writeFile(filePath, codeToWrite);
7337
7434
  const pageIdx = dsm.getConfig().pages.findIndex((p) => p.id === page.id);
@@ -7342,7 +7439,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7342
7439
  cm.updateConfig(cfg);
7343
7440
  pm.updateConfig(cfg);
7344
7441
  }
7345
- const manifestForAudit = await loadManifest5(projectRoot);
7442
+ const manifestForAudit = await loadManifest6(projectRoot);
7346
7443
  await warnInlineDuplicates(projectRoot, page.name || page.id || route.slice(1), codeToWrite, manifestForAudit);
7347
7444
  const relFilePath = routeToRelPath(route, isAuth);
7348
7445
  printPostGenerationReport({
@@ -7361,7 +7458,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7361
7458
  const errors = issues.filter((i) => i.severity === "error");
7362
7459
  if (errors.length >= 5 && aiProvider) {
7363
7460
  console.log(
7364
- chalk10.yellow(`
7461
+ chalk11.yellow(`
7365
7462
  \u{1F504} ${errors.length} quality errors \u2014 attempting AI fix for ${page.name || page.id}...`)
7366
7463
  );
7367
7464
  try {
@@ -7383,7 +7480,7 @@ Rules:
7383
7480
  if (recheckErrors.length < errors.length) {
7384
7481
  codeToWrite = fixedCode2;
7385
7482
  await writeFile(filePath, codeToWrite);
7386
- console.log(chalk10.green(` \u2714 Quality fix: ${errors.length} \u2192 ${recheckErrors.length} errors`));
7483
+ console.log(chalk11.green(` \u2714 Quality fix: ${errors.length} \u2192 ${recheckErrors.length} errors`));
7387
7484
  }
7388
7485
  }
7389
7486
  }
@@ -7392,9 +7489,9 @@ Rules:
7392
7489
  }
7393
7490
  const report = formatIssues(issues);
7394
7491
  if (report) {
7395
- console.log(chalk10.yellow(`
7492
+ console.log(chalk11.yellow(`
7396
7493
  \u{1F50D} Quality check for ${page.name || page.id}:`));
7397
- console.log(chalk10.dim(report));
7494
+ console.log(chalk11.dim(report));
7398
7495
  }
7399
7496
  }
7400
7497
  }
@@ -7409,9 +7506,9 @@ Rules:
7409
7506
  const changes = request.changes;
7410
7507
  const instruction = originalMessage || (typeof changes?.instruction === "string" ? changes.instruction : void 0);
7411
7508
  let resolvedPageCode = typeof changes?.pageCode === "string" && changes.pageCode.trim() !== "" ? changes.pageCode : void 0;
7412
- if (DEBUG2 && instruction) console.log(chalk10.dim(` [update-page] instruction: ${instruction.slice(0, 120)}...`));
7509
+ if (DEBUG2 && instruction) console.log(chalk11.dim(` [update-page] instruction: ${instruction.slice(0, 120)}...`));
7413
7510
  if (DEBUG2 && resolvedPageCode)
7414
- console.log(chalk10.dim(` [update-page] has pageCode (${resolvedPageCode.length} chars)`));
7511
+ console.log(chalk11.dim(` [update-page] has pageCode (${resolvedPageCode.length} chars)`));
7415
7512
  const configChanges = { ...changes };
7416
7513
  delete configChanges.pageCode;
7417
7514
  delete configChanges.pageType;
@@ -7435,12 +7532,12 @@ Rules:
7435
7532
  try {
7436
7533
  currentCode = await readFile(absPath);
7437
7534
  } catch {
7438
- if (DEBUG2) console.log(chalk10.dim(` [update-page] Could not read current file at ${absPath}`));
7535
+ if (DEBUG2) console.log(chalk11.dim(` [update-page] Could not read current file at ${absPath}`));
7439
7536
  }
7440
7537
  if (currentCode) {
7441
7538
  const ai = await createAIProvider(aiProvider ?? "auto");
7442
7539
  if (ai.editPageCode) {
7443
- console.log(chalk10.dim(" \u270F\uFE0F Applying changes to existing page..."));
7540
+ console.log(chalk11.dim(" \u270F\uFE0F Applying changes to existing page..."));
7444
7541
  const coreRules = CORE_CONSTRAINTS;
7445
7542
  const qualityRules = DESIGN_QUALITY;
7446
7543
  const contextualRules = selectContextualRules(instruction);
@@ -7460,9 +7557,9 @@ ${contextualRules}
7460
7557
  ${routeRules}
7461
7558
  ${pagesCtx}`
7462
7559
  );
7463
- if (DEBUG2) console.log(chalk10.dim(` [update-page] AI returned ${resolvedPageCode.length} chars`));
7560
+ if (DEBUG2) console.log(chalk11.dim(` [update-page] AI returned ${resolvedPageCode.length} chars`));
7464
7561
  } else {
7465
- console.log(chalk10.yellow(" \u26A0 AI provider does not support editPageCode"));
7562
+ console.log(chalk11.yellow(" \u26A0 AI provider does not support editPageCode"));
7466
7563
  }
7467
7564
  }
7468
7565
  }
@@ -7492,10 +7589,13 @@ ${pagesCtx}`
7492
7589
  let codeToWrite = fixedCode;
7493
7590
  const { code: autoFixed, fixes: autoFixes } = await autoFixCode(codeToWrite);
7494
7591
  codeToWrite = autoFixed;
7592
+ const { code: layoutStripped, stripped } = stripInlineLayoutElements(codeToWrite);
7593
+ codeToWrite = layoutStripped;
7495
7594
  const allFixes = [...postFixes, ...autoFixes];
7595
+ if (stripped.length > 0) allFixes.push(`stripped inline ${stripped.join(", ")} (layout owns these)`);
7496
7596
  if (allFixes.length > 0) {
7497
- console.log(chalk10.dim(" \u{1F527} Post-generation fixes:"));
7498
- allFixes.forEach((f) => console.log(chalk10.dim(` ${f}`)));
7597
+ console.log(chalk11.dim(" \u{1F527} Post-generation fixes:"));
7598
+ allFixes.forEach((f) => console.log(chalk11.dim(` ${f}`)));
7499
7599
  }
7500
7600
  await writeFile(absPath, codeToWrite);
7501
7601
  const updatePageIdx = dsm.getConfig().pages.findIndex((p) => p.id === pageDef.id);
@@ -7506,7 +7606,7 @@ ${pagesCtx}`
7506
7606
  cm.updateConfig(cfg);
7507
7607
  pm.updateConfig(cfg);
7508
7608
  }
7509
- const manifestForAudit = await loadManifest5(projectRoot);
7609
+ const manifestForAudit = await loadManifest6(projectRoot);
7510
7610
  await warnInlineDuplicates(
7511
7611
  projectRoot,
7512
7612
  pageDef.name || pageDef.id || route.slice(1),
@@ -7528,9 +7628,9 @@ ${pagesCtx}`
7528
7628
  const issues = validatePageQuality(codeToWrite);
7529
7629
  const report = formatIssues(issues);
7530
7630
  if (report) {
7531
- console.log(chalk10.yellow(`
7631
+ console.log(chalk11.yellow(`
7532
7632
  \u{1F50D} Quality check for ${pageDef.name || pageDef.id}:`));
7533
- console.log(chalk10.dim(report));
7633
+ console.log(chalk11.dim(report));
7534
7634
  }
7535
7635
  } else {
7536
7636
  try {
@@ -7539,11 +7639,11 @@ ${pagesCtx}`
7539
7639
  if (fixes.length > 0) {
7540
7640
  code = fixed;
7541
7641
  await writeFile(absPath, code);
7542
- console.log(chalk10.dim(" \u{1F527} Auto-fixes applied:"));
7543
- fixes.forEach((f) => console.log(chalk10.dim(` ${f}`)));
7642
+ console.log(chalk11.dim(" \u{1F527} Auto-fixes applied:"));
7643
+ fixes.forEach((f) => console.log(chalk11.dim(` ${f}`)));
7544
7644
  }
7545
7645
  const relFilePath = routeToRelPath(route, isAuth);
7546
- const manifest = await loadManifest5(projectRoot);
7646
+ const manifest = await loadManifest6(projectRoot);
7547
7647
  printPostGenerationReport({
7548
7648
  action: "updated",
7549
7649
  pageTitle: pageDef.name || pageDef.id || "Page",
@@ -7557,9 +7657,9 @@ ${pagesCtx}`
7557
7657
  const issues = validatePageQuality(code);
7558
7658
  const report = formatIssues(issues);
7559
7659
  if (report) {
7560
- console.log(chalk10.yellow(`
7660
+ console.log(chalk11.yellow(`
7561
7661
  \u{1F50D} Quality check for ${pageDef.name || pageDef.id}:`));
7562
- console.log(chalk10.dim(report));
7662
+ console.log(chalk11.dim(report));
7563
7663
  }
7564
7664
  } catch {
7565
7665
  }
@@ -7588,236 +7688,10 @@ ${pagesCtx}`
7588
7688
  }
7589
7689
  }
7590
7690
 
7591
- // src/commands/chat/layout-extractor.ts
7592
- import { readFileSync as readFileSync9, readdirSync as readdirSync2 } from "fs";
7593
- import { join as join9, resolve as resolve8 } from "path";
7594
- import chalk11 from "chalk";
7595
- import { loadManifest as loadManifest6, generateSharedComponent as generateSharedComponent3, integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout3 } from "@getcoherent/core";
7596
-
7597
- // src/commands/chat/jsx-extractor.ts
7598
- function extractBalancedTag(source, tagName) {
7599
- const openRe = new RegExp(`<${tagName}\\b`, "gi");
7600
- const match = openRe.exec(source);
7601
- if (!match) return null;
7602
- const startIdx = match.index;
7603
- const openTagRe = new RegExp(`<${tagName}\\b`, "gi");
7604
- const closeTagRe = new RegExp(`</${tagName}>`, "gi");
7605
- const events = [];
7606
- let m;
7607
- openTagRe.lastIndex = startIdx;
7608
- while ((m = openTagRe.exec(source)) !== null) {
7609
- events.push({ pos: m.index, type: "open", end: m.index + m[0].length });
7610
- }
7611
- closeTagRe.lastIndex = startIdx;
7612
- while ((m = closeTagRe.exec(source)) !== null) {
7613
- events.push({ pos: m.index, type: "close", end: m.index + m[0].length });
7614
- }
7615
- events.sort((a, b) => a.pos - b.pos);
7616
- let depth = 0;
7617
- for (const ev of events) {
7618
- if (ev.pos < startIdx) continue;
7619
- if (ev.type === "open") depth++;
7620
- else {
7621
- depth--;
7622
- if (depth === 0) return source.slice(startIdx, ev.end);
7623
- }
7624
- }
7625
- return null;
7626
- }
7627
- function extractRelevantImports(fullSource, jsxBlock) {
7628
- const importLines = [];
7629
- const importRe = /^import\s+.*from\s+['"][^'"]+['"];?\s*$/gm;
7630
- let m;
7631
- while ((m = importRe.exec(fullSource)) !== null) {
7632
- const line = m[0];
7633
- const namesMatch = line.match(/import\s*\{([^}]+)\}/);
7634
- if (namesMatch) {
7635
- const names = namesMatch[1].split(",").map(
7636
- (n) => n.trim().split(/\s+as\s+/).pop().trim()
7637
- );
7638
- if (names.some((name) => jsxBlock.includes(name))) {
7639
- importLines.push(line);
7640
- }
7641
- }
7642
- const defaultMatch = line.match(/import\s+(\w+)\s+from/);
7643
- if (defaultMatch && jsxBlock.includes(defaultMatch[1])) {
7644
- importLines.push(line);
7645
- }
7646
- }
7647
- return [...new Set(importLines)];
7648
- }
7649
- function extractStateHooks(fullSource, jsxBlock) {
7650
- const hooks = [];
7651
- const stateRe = /const\s+\[(\w+),\s*(\w+)\]\s*=\s*useState\b[^)]*\)/g;
7652
- let m;
7653
- while ((m = stateRe.exec(fullSource)) !== null) {
7654
- const [fullMatch, getter, setter] = m;
7655
- if (jsxBlock.includes(getter) || jsxBlock.includes(setter)) {
7656
- hooks.push(fullMatch);
7657
- }
7658
- }
7659
- return hooks;
7660
- }
7661
- function addActiveNavToHeader(code) {
7662
- let result = code;
7663
- if (!result.includes("usePathname")) {
7664
- if (result.includes("from 'next/navigation'")) {
7665
- result = result.replace(
7666
- /import\s*\{([^}]+)\}\s*from\s*'next\/navigation'/,
7667
- (_, names) => `import { ${names.trim()}, usePathname } from 'next/navigation'`
7668
- );
7669
- } else {
7670
- result = result.replace(
7671
- "export function Header()",
7672
- "import { usePathname } from 'next/navigation'\n\nexport function Header()"
7673
- );
7674
- }
7675
- }
7676
- if (!result.includes("const pathname")) {
7677
- result = result.replace(
7678
- /export function Header\(\)\s*\{/,
7679
- "export function Header() {\n const pathname = usePathname()"
7680
- );
7681
- }
7682
- result = result.replace(
7683
- /<Link\s+href="(\/[^"]*?)"\s+className="([^"]*?)(?:text-foreground|text-muted-foreground(?:\s+hover:text-foreground)?(?:\s+transition-colors)?)([^"]*?)">/g,
7684
- (_, href, before, after) => {
7685
- const base = before.trim();
7686
- const trail = after.trim();
7687
- const staticParts = [base, trail].filter(Boolean).join(" ");
7688
- const space = staticParts ? " " : "";
7689
- return `<Link href="${href}" className={\`${staticParts}${space}\${pathname === '${href}' ? 'text-foreground font-medium' : 'text-muted-foreground hover:text-foreground transition-colors'}\`}>`;
7690
- }
7691
- );
7692
- return result;
7693
- }
7694
-
7695
- // src/commands/chat/layout-extractor.ts
7696
- function findAllPageFiles(dir) {
7697
- const results = [];
7698
- try {
7699
- for (const entry of readdirSync2(dir, { withFileTypes: true })) {
7700
- const full = join9(dir, entry.name);
7701
- if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".next" && entry.name !== "design-system") {
7702
- results.push(...findAllPageFiles(full));
7703
- } else if (entry.name === "page.tsx" || entry.name === "page.jsx") {
7704
- results.push(full);
7705
- }
7706
- }
7707
- } catch {
7708
- }
7709
- return results;
7710
- }
7711
- async function extractAndShareLayoutComponents(projectRoot, generatedPageFiles) {
7712
- const manifest = await loadManifest6(projectRoot);
7713
- const hasSharedHeader = manifest.shared.some((c) => c.type === "layout" && /header|nav/i.test(c.name));
7714
- const hasSharedFooter = manifest.shared.some((c) => c.type === "layout" && /footer/i.test(c.name));
7715
- if (hasSharedHeader && hasSharedFooter) return false;
7716
- let sourceCode = "";
7717
- for (const file of generatedPageFiles) {
7718
- try {
7719
- const code = readFileSync9(file, "utf-8");
7720
- if (code.includes("<header") || code.includes("<footer") || code.includes("<nav")) {
7721
- sourceCode = code;
7722
- break;
7723
- }
7724
- } catch {
7725
- continue;
7726
- }
7727
- }
7728
- if (!sourceCode) return false;
7729
- let extracted = false;
7730
- if (!hasSharedHeader) {
7731
- let headerJsx = extractBalancedTag(sourceCode, "header");
7732
- if (!headerJsx) headerJsx = extractBalancedTag(sourceCode, "nav");
7733
- if (headerJsx) {
7734
- const imports = extractRelevantImports(sourceCode, headerJsx);
7735
- const importBlock = imports.length > 0 ? imports.join("\n") + "\n" : "import Link from 'next/link'\n";
7736
- const stateHooks = extractStateHooks(sourceCode, headerJsx);
7737
- const needsReactImport = stateHooks.length > 0 && !importBlock.includes("from 'react'");
7738
- const reactImport = needsReactImport ? "import { useState } from 'react'\n" : "";
7739
- const stateBlock = stateHooks.length > 0 ? " " + stateHooks.join("\n ") + "\n" : "";
7740
- const returnIndent = stateBlock ? " " : " ";
7741
- let headerComponent = `'use client'
7742
-
7743
- ${reactImport}${importBlock}
7744
- export function Header() {
7745
- ${stateBlock}${returnIndent}return (
7746
- ${headerJsx}
7747
- )
7748
- }
7749
- `;
7750
- headerComponent = addActiveNavToHeader(headerComponent);
7751
- await generateSharedComponent3(projectRoot, {
7752
- name: "Header",
7753
- type: "layout",
7754
- code: headerComponent,
7755
- description: "Main site header/navigation",
7756
- usedIn: ["app/layout.tsx"]
7757
- });
7758
- extracted = true;
7759
- }
7760
- }
7761
- if (!hasSharedFooter) {
7762
- const footerJsx = extractBalancedTag(sourceCode, "footer");
7763
- if (footerJsx) {
7764
- const imports = extractRelevantImports(sourceCode, footerJsx);
7765
- const importBlock = imports.length > 0 ? imports.join("\n") + "\n" : "import Link from 'next/link'\n";
7766
- const stateHooks = extractStateHooks(sourceCode, footerJsx);
7767
- const needsReactImport = stateHooks.length > 0 && !importBlock.includes("from 'react'");
7768
- const reactImport = needsReactImport ? "import { useState } from 'react'\n" : "";
7769
- const stateBlock = stateHooks.length > 0 ? " " + stateHooks.join("\n ") + "\n" : "";
7770
- const returnIndent = stateBlock ? " " : " ";
7771
- const footerComponent = `'use client'
7772
-
7773
- ${reactImport}${importBlock}
7774
- export function Footer() {
7775
- ${stateBlock}${returnIndent}return (
7776
- ${footerJsx}
7777
- )
7778
- }
7779
- `;
7780
- await generateSharedComponent3(projectRoot, {
7781
- name: "Footer",
7782
- type: "layout",
7783
- code: footerComponent,
7784
- description: "Site footer",
7785
- usedIn: ["app/layout.tsx"]
7786
- });
7787
- extracted = true;
7788
- }
7789
- }
7790
- if (!extracted) return false;
7791
- await integrateSharedLayoutIntoRootLayout3(projectRoot);
7792
- await ensureAuthRouteGroup(projectRoot);
7793
- const allPageFiles = /* @__PURE__ */ new Set([...generatedPageFiles, ...findAllPageFiles(resolve8(projectRoot, "app"))]);
7794
- for (const file of allPageFiles) {
7795
- try {
7796
- let code = await readFile(file);
7797
- const original = code;
7798
- const headerBlock = extractBalancedTag(code, "header");
7799
- if (headerBlock) {
7800
- code = code.replace(headerBlock, "");
7801
- } else {
7802
- const navBlock = extractBalancedTag(code, "nav");
7803
- if (navBlock) code = code.replace(navBlock, "");
7804
- }
7805
- const footerBlock = extractBalancedTag(code, "footer");
7806
- if (footerBlock) code = code.replace(footerBlock, "");
7807
- code = code.replace(/\n{3,}/g, "\n\n");
7808
- if (code !== original) await writeFile(file, code);
7809
- } catch {
7810
- continue;
7811
- }
7812
- }
7813
- console.log(chalk11.cyan(" \u{1F517} Extracted Header and Footer as shared components (all pages via layout)"));
7814
- return true;
7815
- }
7816
-
7817
7691
  // src/commands/chat/interactive.ts
7818
7692
  import chalk12 from "chalk";
7819
- import { resolve as resolve9 } from "path";
7820
- import { existsSync as existsSync15, readFileSync as readFileSync10, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5 } from "fs";
7693
+ import { resolve as resolve8 } from "path";
7694
+ import { existsSync as existsSync15, readFileSync as readFileSync9, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5 } from "fs";
7821
7695
  import { DesignSystemManager as DesignSystemManager6, ComponentManager as ComponentManager3, loadManifest as loadManifest7 } from "@getcoherent/core";
7822
7696
  var DEBUG3 = process.env.COHERENT_DEBUG === "1";
7823
7697
  async function interactiveChat(options, chatCommandFn) {
@@ -7837,13 +7711,13 @@ async function interactiveChat(options, chatCommandFn) {
7837
7711
  \u274C Invalid provider: ${options.provider}`));
7838
7712
  process.exit(1);
7839
7713
  }
7840
- const historyDir = resolve9(homedir2(), ".coherent");
7841
- const historyFile = resolve9(historyDir, "history");
7714
+ const historyDir = resolve8(homedir2(), ".coherent");
7715
+ const historyFile = resolve8(historyDir, "history");
7842
7716
  let history = [];
7843
7717
  try {
7844
7718
  mkdirSync5(historyDir, { recursive: true });
7845
7719
  if (existsSync15(historyFile)) {
7846
- history = readFileSync10(historyFile, "utf-8").split("\n").filter(Boolean).slice(-200);
7720
+ history = readFileSync9(historyFile, "utf-8").split("\n").filter(Boolean).slice(-200);
7847
7721
  }
7848
7722
  } catch (e) {
7849
7723
  if (DEBUG3) console.error("Failed to load REPL history:", e);
@@ -8067,7 +7941,7 @@ async function chatCommand(message, options) {
8067
7941
  }
8068
7942
  if (/switch to light mode|default to light|make.*light.*(default|theme)|light theme/i.test(message)) {
8069
7943
  spinner.start("Setting default theme to light...");
8070
- const layoutPath = resolve10(projectRoot, "app/layout.tsx");
7944
+ const layoutPath = resolve9(projectRoot, "app/layout.tsx");
8071
7945
  try {
8072
7946
  let layout = await readFile(layoutPath);
8073
7947
  layout = layout.replace(/className="dark"/, "");
@@ -8106,7 +7980,7 @@ async function chatCommand(message, options) {
8106
7980
  spinner.start("Parsing your request...");
8107
7981
  let manifest = await loadManifest8(project.root);
8108
7982
  const validShared = manifest.shared.filter((s) => {
8109
- const fp = resolve10(project.root, s.file);
7983
+ const fp = resolve9(project.root, s.file);
8110
7984
  return existsSync16(fp);
8111
7985
  });
8112
7986
  if (validShared.length !== manifest.shared.length) {
@@ -8402,21 +8276,6 @@ async function chatCommand(message, options) {
8402
8276
  const result = await applyModification(request, dsm, cm, pm, projectRoot, provider, message);
8403
8277
  results.push(result);
8404
8278
  }
8405
- const anyPageGenerated = normalizedRequests.some(
8406
- (req, i) => (req.type === "add-page" || req.type === "update-page") && results[i]?.success
8407
- );
8408
- if (anyPageGenerated) {
8409
- const generatedPageFiles = normalizedRequests.filter((req, i) => (req.type === "add-page" || req.type === "update-page") && results[i]?.success).map((req) => {
8410
- const page = req.changes;
8411
- const route = page.route || `/${page.id || "page"}`;
8412
- return routeToFsPath(projectRoot, route, isAuthRoute(route));
8413
- }).filter((f) => existsSync16(f));
8414
- try {
8415
- await extractAndShareLayoutComponents(projectRoot, generatedPageFiles);
8416
- } catch (err) {
8417
- if (DEBUG4) console.log(chalk13.dim("Shared layout extraction failed:", err));
8418
- }
8419
- }
8420
8279
  const currentConfig = dsm.getConfig();
8421
8280
  const autoScaffoldEnabled = currentConfig.settings.autoScaffold === true;
8422
8281
  const scaffoldedPages = [];
@@ -8430,7 +8289,7 @@ async function chatCommand(message, options) {
8430
8289
  let pageCode = "";
8431
8290
  if (existsSync16(pageFilePath)) {
8432
8291
  try {
8433
- pageCode = readFileSync11(pageFilePath, "utf-8");
8292
+ pageCode = readFileSync10(pageFilePath, "utf-8");
8434
8293
  } catch {
8435
8294
  }
8436
8295
  }
@@ -8507,7 +8366,7 @@ async function chatCommand(message, options) {
8507
8366
  const isAuth = isAuthRoute(linkedRoute);
8508
8367
  const filePath = routeToFsPath(projectRoot, linkedRoute, isAuth);
8509
8368
  if (isAuth) await ensureAuthRouteGroup(projectRoot);
8510
- const dir = resolve10(filePath, "..");
8369
+ const dir = resolve9(filePath, "..");
8511
8370
  if (!existsSync16(dir)) {
8512
8371
  mkdirSync6(dir, { recursive: true });
8513
8372
  }
@@ -8543,7 +8402,7 @@ async function chatCommand(message, options) {
8543
8402
  dsm.updateConfig(latestConfig);
8544
8403
  if (DEBUG4) console.log(chalk13.dim(` [theme] Set defaultMode to "${targetMode}"`));
8545
8404
  }
8546
- const layoutPath = resolve10(projectRoot, "app", "layout.tsx");
8405
+ const layoutPath = resolve9(projectRoot, "app", "layout.tsx");
8547
8406
  try {
8548
8407
  let layoutCode = await readFile(layoutPath);
8549
8408
  if (targetMode === "dark" && !layoutCode.includes('className="dark"')) {
@@ -8598,7 +8457,7 @@ async function chatCommand(message, options) {
8598
8457
  console.log("");
8599
8458
  }
8600
8459
  if (uxRecommendations) {
8601
- const recPath = resolve10(projectRoot, "recommendations.md");
8460
+ const recPath = resolve9(projectRoot, "recommendations.md");
8602
8461
  const section = `
8603
8462
 
8604
8463
  ---
@@ -8682,19 +8541,19 @@ ${uxRecommendations}
8682
8541
  import chalk14 from "chalk";
8683
8542
  import ora3 from "ora";
8684
8543
  import { spawn } from "child_process";
8685
- import { existsSync as existsSync19, rmSync as rmSync3, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
8686
- import { resolve as resolve11, join as join12 } from "path";
8544
+ import { existsSync as existsSync19, rmSync as rmSync3, readFileSync as readFileSync13, writeFileSync as writeFileSync10 } from "fs";
8545
+ import { resolve as resolve10, join as join11 } from "path";
8687
8546
  import { readdir as readdir2 } from "fs/promises";
8688
8547
  import { DesignSystemManager as DesignSystemManager8, ComponentGenerator as ComponentGenerator3 } from "@getcoherent/core";
8689
8548
 
8690
8549
  // src/utils/file-watcher.ts
8691
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, existsSync as existsSync18 } from "fs";
8692
- import { relative as relative3, join as join11 } from "path";
8550
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, existsSync as existsSync18 } from "fs";
8551
+ import { relative as relative3, join as join10 } from "path";
8693
8552
  import { loadManifest as loadManifest9, saveManifest as saveManifest3 } from "@getcoherent/core";
8694
8553
 
8695
8554
  // src/utils/component-integrity.ts
8696
- import { existsSync as existsSync17, readFileSync as readFileSync12, readdirSync as readdirSync3 } from "fs";
8697
- import { join as join10, relative as relative2 } from "path";
8555
+ import { existsSync as existsSync17, readFileSync as readFileSync11, readdirSync as readdirSync2 } from "fs";
8556
+ import { join as join9, relative as relative2 } from "path";
8698
8557
  function extractExportedComponentNames(code) {
8699
8558
  const names = [];
8700
8559
  let m;
@@ -8720,14 +8579,14 @@ function arraysEqual(a, b) {
8720
8579
  }
8721
8580
  function findPagesImporting(projectRoot, componentName, componentFile) {
8722
8581
  const results = [];
8723
- const appDir = join10(projectRoot, "app");
8582
+ const appDir = join9(projectRoot, "app");
8724
8583
  if (!existsSync17(appDir)) return results;
8725
8584
  const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
8726
8585
  const componentImportPath = componentFile.replace(/\.tsx$/, "").replace(/\.jsx$/, "");
8727
8586
  for (const absPath of pageFiles) {
8728
8587
  if (absPath.includes("design-system")) continue;
8729
8588
  try {
8730
- const code = readFileSync12(absPath, "utf-8");
8589
+ const code = readFileSync11(absPath, "utf-8");
8731
8590
  const hasNamedImport = new RegExp(`import\\s+\\{[^}]*\\b${componentName}\\b[^}]*\\}\\s+from\\s+['"]`).test(code);
8732
8591
  const hasDefaultImport = new RegExp(`import\\s+${componentName}\\s+from\\s+['"]`).test(code);
8733
8592
  const hasPathImport = code.includes(`@/${componentImportPath}`);
@@ -8740,10 +8599,10 @@ function findPagesImporting(projectRoot, componentName, componentFile) {
8740
8599
  return results;
8741
8600
  }
8742
8601
  function isUsedInLayout(projectRoot, componentName) {
8743
- const layoutPath = join10(projectRoot, "app", "layout.tsx");
8602
+ const layoutPath = join9(projectRoot, "app", "layout.tsx");
8744
8603
  if (!existsSync17(layoutPath)) return false;
8745
8604
  try {
8746
- const code = readFileSync12(layoutPath, "utf-8");
8605
+ const code = readFileSync11(layoutPath, "utf-8");
8747
8606
  return code.includes(componentName);
8748
8607
  } catch {
8749
8608
  return false;
@@ -8751,7 +8610,7 @@ function isUsedInLayout(projectRoot, componentName) {
8751
8610
  }
8752
8611
  function findUnregisteredComponents(projectRoot, manifest) {
8753
8612
  const results = [];
8754
- const componentsDir = join10(projectRoot, "components");
8613
+ const componentsDir = join9(projectRoot, "components");
8755
8614
  if (!existsSync17(componentsDir)) return results;
8756
8615
  const registeredFiles = new Set(manifest.shared.map((s) => s.file));
8757
8616
  const registeredNames = new Set(manifest.shared.map((s) => s.name));
@@ -8764,7 +8623,7 @@ function findUnregisteredComponents(projectRoot, manifest) {
8764
8623
  const relFile = relative2(projectRoot, absPath);
8765
8624
  if (registeredFiles.has(relFile)) continue;
8766
8625
  try {
8767
- const code = readFileSync12(absPath, "utf-8");
8626
+ const code = readFileSync11(absPath, "utf-8");
8768
8627
  const exports = extractExportedComponentNames(code);
8769
8628
  for (const name of exports) {
8770
8629
  if (registeredNames.has(name)) continue;
@@ -8779,14 +8638,14 @@ function findUnregisteredComponents(projectRoot, manifest) {
8779
8638
  }
8780
8639
  function findInlineDuplicates(projectRoot, manifest) {
8781
8640
  const results = [];
8782
- const appDir = join10(projectRoot, "app");
8641
+ const appDir = join9(projectRoot, "app");
8783
8642
  if (!existsSync17(appDir)) return results;
8784
8643
  const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
8785
8644
  for (const absPath of pageFiles) {
8786
8645
  if (absPath.includes("design-system")) continue;
8787
8646
  let code;
8788
8647
  try {
8789
- code = readFileSync12(absPath, "utf-8");
8648
+ code = readFileSync11(absPath, "utf-8");
8790
8649
  } catch {
8791
8650
  continue;
8792
8651
  }
@@ -8809,7 +8668,7 @@ function findInlineDuplicates(projectRoot, manifest) {
8809
8668
  return results;
8810
8669
  }
8811
8670
  function findComponentFileByExportName(projectRoot, componentName) {
8812
- const componentsDir = join10(projectRoot, "components");
8671
+ const componentsDir = join9(projectRoot, "components");
8813
8672
  if (!existsSync17(componentsDir)) return null;
8814
8673
  const files = collectFiles(
8815
8674
  componentsDir,
@@ -8818,7 +8677,7 @@ function findComponentFileByExportName(projectRoot, componentName) {
8818
8677
  );
8819
8678
  for (const absPath of files) {
8820
8679
  try {
8821
- const code = readFileSync12(absPath, "utf-8");
8680
+ const code = readFileSync11(absPath, "utf-8");
8822
8681
  const exports = extractExportedComponentNames(code);
8823
8682
  if (exports.includes(componentName)) {
8824
8683
  return relative2(projectRoot, absPath);
@@ -8831,7 +8690,7 @@ function findComponentFileByExportName(projectRoot, componentName) {
8831
8690
  function removeOrphanedEntries(projectRoot, manifest) {
8832
8691
  const removed = [];
8833
8692
  const valid = manifest.shared.filter((entry) => {
8834
- const filePath = join10(projectRoot, entry.file);
8693
+ const filePath = join9(projectRoot, entry.file);
8835
8694
  if (existsSync17(filePath)) return true;
8836
8695
  removed.push({ id: entry.id, name: entry.name });
8837
8696
  return false;
@@ -8850,7 +8709,7 @@ function reconcileComponents(projectRoot, manifest) {
8850
8709
  };
8851
8710
  const m = { ...manifest, shared: [...manifest.shared], nextId: manifest.nextId };
8852
8711
  m.shared = m.shared.filter((entry) => {
8853
- const filePath = join10(projectRoot, entry.file);
8712
+ const filePath = join9(projectRoot, entry.file);
8854
8713
  if (!existsSync17(filePath)) {
8855
8714
  const newPath = findComponentFileByExportName(projectRoot, entry.name);
8856
8715
  if (newPath) {
@@ -8863,7 +8722,7 @@ function reconcileComponents(projectRoot, manifest) {
8863
8722
  }
8864
8723
  let code;
8865
8724
  try {
8866
- code = readFileSync12(join10(projectRoot, entry.file), "utf-8");
8725
+ code = readFileSync11(join9(projectRoot, entry.file), "utf-8");
8867
8726
  } catch {
8868
8727
  return true;
8869
8728
  }
@@ -8940,12 +8799,12 @@ function collectFiles(dir, filter, skipDirs = []) {
8940
8799
  function walk(d) {
8941
8800
  let entries;
8942
8801
  try {
8943
- entries = readdirSync3(d, { withFileTypes: true });
8802
+ entries = readdirSync2(d, { withFileTypes: true });
8944
8803
  } catch {
8945
8804
  return;
8946
8805
  }
8947
8806
  for (const e of entries) {
8948
- const full = join10(d, e.name);
8807
+ const full = join9(d, e.name);
8949
8808
  if (e.isDirectory()) {
8950
8809
  if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
8951
8810
  walk(full);
@@ -8984,9 +8843,9 @@ function findInlineDuplicatesOfShared(content, manifest) {
8984
8843
  }
8985
8844
  function getWatcherConfig(projectRoot) {
8986
8845
  try {
8987
- const pkgPath = join11(projectRoot, "package.json");
8846
+ const pkgPath = join10(projectRoot, "package.json");
8988
8847
  if (!existsSync18(pkgPath)) return defaultWatcherConfig();
8989
- const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
8848
+ const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
8990
8849
  const c = pkg?.coherent?.watcher ?? {};
8991
8850
  return {
8992
8851
  enabled: c.enabled !== false,
@@ -9014,7 +8873,7 @@ async function handleFileChange(projectRoot, filePath) {
9014
8873
  if (relativePath.includes("node_modules") || relativePath.includes(".next")) return;
9015
8874
  let content;
9016
8875
  try {
9017
- content = readFileSync13(filePath, "utf-8");
8876
+ content = readFileSync12(filePath, "utf-8");
9018
8877
  } catch {
9019
8878
  return;
9020
8879
  }
@@ -9087,7 +8946,7 @@ async function detectNewComponent(projectRoot, filePath) {
9087
8946
  const manifest = await loadManifest9(projectRoot);
9088
8947
  const alreadyRegistered = manifest.shared.some((s) => s.file === relativePath);
9089
8948
  if (alreadyRegistered) return;
9090
- const code = readFileSync13(filePath, "utf-8");
8949
+ const code = readFileSync12(filePath, "utf-8");
9091
8950
  const exports = extractExportedComponentNames(code);
9092
8951
  if (exports.length > 0) {
9093
8952
  const alreadyByName = exports.every((n) => manifest.shared.some((s) => s.name === n));
@@ -9113,8 +8972,8 @@ function startFileWatcher(projectRoot) {
9113
8972
  let watcher = null;
9114
8973
  let manifestWatcher = null;
9115
8974
  import("chokidar").then((chokidar) => {
9116
- const appGlob = join11(projectRoot, "app", "**", "*.tsx");
9117
- const compGlob = join11(projectRoot, "components", "**", "*.tsx");
8975
+ const appGlob = join10(projectRoot, "app", "**", "*.tsx");
8976
+ const compGlob = join10(projectRoot, "components", "**", "*.tsx");
9118
8977
  watcher = chokidar.default.watch([appGlob, compGlob], {
9119
8978
  ignoreInitial: true,
9120
8979
  awaitWriteFinish: { stabilityThreshold: 500 }
@@ -9126,7 +8985,7 @@ function startFileWatcher(projectRoot) {
9126
8985
  });
9127
8986
  watcher.on("unlink", (fp) => handleFileDelete(projectRoot, fp));
9128
8987
  });
9129
- const manifestPath = join11(projectRoot, "coherent.components.json");
8988
+ const manifestPath = join10(projectRoot, "coherent.components.json");
9130
8989
  if (existsSync18(manifestPath)) {
9131
8990
  import("chokidar").then((chokidar) => {
9132
8991
  manifestWatcher = chokidar.default.watch(manifestPath, { ignoreInitial: true });
@@ -9141,7 +9000,7 @@ function startFileWatcher(projectRoot) {
9141
9000
 
9142
9001
  // src/commands/preview.ts
9143
9002
  function getPackageManager(projectRoot) {
9144
- const hasPnpm = existsSync19(resolve11(projectRoot, "pnpm-lock.yaml"));
9003
+ const hasPnpm = existsSync19(resolve10(projectRoot, "pnpm-lock.yaml"));
9145
9004
  return hasPnpm ? "pnpm" : "npm";
9146
9005
  }
9147
9006
  function runInstall(projectRoot) {
@@ -9163,8 +9022,8 @@ function runInstall(projectRoot) {
9163
9022
  });
9164
9023
  }
9165
9024
  function checkProjectInitialized(projectRoot) {
9166
- const configPath = resolve11(projectRoot, "design-system.config.ts");
9167
- const packageJsonPath = resolve11(projectRoot, "package.json");
9025
+ const configPath = resolve10(projectRoot, "design-system.config.ts");
9026
+ const packageJsonPath = resolve10(projectRoot, "package.json");
9168
9027
  if (!existsSync19(configPath)) {
9169
9028
  return false;
9170
9029
  }
@@ -9174,11 +9033,11 @@ function checkProjectInitialized(projectRoot) {
9174
9033
  return true;
9175
9034
  }
9176
9035
  function checkDependenciesInstalled(projectRoot) {
9177
- const nodeModulesPath = resolve11(projectRoot, "node_modules");
9036
+ const nodeModulesPath = resolve10(projectRoot, "node_modules");
9178
9037
  return existsSync19(nodeModulesPath);
9179
9038
  }
9180
9039
  function clearStaleCache(projectRoot) {
9181
- const nextDir = join12(projectRoot, ".next");
9040
+ const nextDir = join11(projectRoot, ".next");
9182
9041
  if (existsSync19(nextDir)) {
9183
9042
  rmSync3(nextDir, { recursive: true, force: true });
9184
9043
  console.log(chalk14.dim(" \u2714 Cleared stale build cache"));
@@ -9199,7 +9058,7 @@ async function listPageFiles(appDir) {
9199
9058
  async function walk(dir) {
9200
9059
  const entries = await readdir2(dir, { withFileTypes: true });
9201
9060
  for (const e of entries) {
9202
- const full = join12(dir, e.name);
9061
+ const full = join11(dir, e.name);
9203
9062
  if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "api" && e.name !== "design-system") await walk(full);
9204
9063
  else if (e.isFile() && e.name === "page.tsx") out.push(full);
9205
9064
  }
@@ -9208,10 +9067,10 @@ async function listPageFiles(appDir) {
9208
9067
  return out;
9209
9068
  }
9210
9069
  async function validateSyntax(projectRoot) {
9211
- const appDir = join12(projectRoot, "app");
9070
+ const appDir = join11(projectRoot, "app");
9212
9071
  const pages = await listPageFiles(appDir);
9213
9072
  for (const file of pages) {
9214
- const content = readFileSync14(file, "utf-8");
9073
+ const content = readFileSync13(file, "utf-8");
9215
9074
  const fixed = fixUnescapedLtInJsx(sanitizeMetadataStrings(ensureUseClientIfNeeded(content)));
9216
9075
  if (fixed !== content) {
9217
9076
  writeFileSync10(file, fixed, "utf-8");
@@ -9220,13 +9079,13 @@ async function validateSyntax(projectRoot) {
9220
9079
  }
9221
9080
  }
9222
9081
  async function fixMissingComponentExports(projectRoot) {
9223
- const appDir = join12(projectRoot, "app");
9224
- const uiDir = join12(projectRoot, "components", "ui");
9082
+ const appDir = join11(projectRoot, "app");
9083
+ const uiDir = join11(projectRoot, "components", "ui");
9225
9084
  if (!existsSync19(appDir) || !existsSync19(uiDir)) return;
9226
9085
  const pages = await listPageFiles(appDir);
9227
9086
  const neededExports = /* @__PURE__ */ new Map();
9228
9087
  for (const file of pages) {
9229
- const content = readFileSync14(file, "utf-8");
9088
+ const content = readFileSync13(file, "utf-8");
9230
9089
  const importRe = /import\s*\{([^}]+)\}\s*from\s*['"]@\/components\/ui\/([^'"]+)['"]/g;
9231
9090
  let m;
9232
9091
  while ((m = importRe.exec(content)) !== null) {
@@ -9236,7 +9095,7 @@ async function fixMissingComponentExports(projectRoot) {
9236
9095
  for (const name of names) neededExports.get(componentId).add(name);
9237
9096
  }
9238
9097
  }
9239
- const configPath = join12(projectRoot, "design-system.config.ts");
9098
+ const configPath = join11(projectRoot, "design-system.config.ts");
9240
9099
  let config2 = null;
9241
9100
  try {
9242
9101
  const mgr = new DesignSystemManager8(configPath);
@@ -9245,7 +9104,7 @@ async function fixMissingComponentExports(projectRoot) {
9245
9104
  }
9246
9105
  const generator = new ComponentGenerator3(config2 || { components: [], pages: [], tokens: {} });
9247
9106
  for (const [componentId, needed] of neededExports) {
9248
- const componentFile = join12(uiDir, `${componentId}.tsx`);
9107
+ const componentFile = join11(uiDir, `${componentId}.tsx`);
9249
9108
  const def = getShadcnComponent(componentId);
9250
9109
  if (!existsSync19(componentFile)) {
9251
9110
  if (!def) continue;
@@ -9259,7 +9118,7 @@ async function fixMissingComponentExports(projectRoot) {
9259
9118
  }
9260
9119
  continue;
9261
9120
  }
9262
- const content = readFileSync14(componentFile, "utf-8");
9121
+ const content = readFileSync13(componentFile, "utf-8");
9263
9122
  const exportRe = /export\s+(?:const|function|class)\s+(\w+)|export\s*\{([^}]+)\}/g;
9264
9123
  const existingExports = /* @__PURE__ */ new Set();
9265
9124
  let em;
@@ -9282,7 +9141,7 @@ async function fixMissingComponentExports(projectRoot) {
9282
9141
  }
9283
9142
  }
9284
9143
  async function backfillPageAnalysis(projectRoot) {
9285
- const configPath = join12(projectRoot, "design-system.config.ts");
9144
+ const configPath = join11(projectRoot, "design-system.config.ts");
9286
9145
  if (!existsSync19(configPath)) return;
9287
9146
  try {
9288
9147
  const mgr = new DesignSystemManager8(configPath);
@@ -9294,14 +9153,14 @@ async function backfillPageAnalysis(projectRoot) {
9294
9153
  const isAuth = route.includes("login") || route.includes("register") || route.includes("signup") || route.includes("sign-up");
9295
9154
  let filePath;
9296
9155
  if (route === "/") {
9297
- filePath = join12(projectRoot, "app", "page.tsx");
9156
+ filePath = join11(projectRoot, "app", "page.tsx");
9298
9157
  } else if (isAuth) {
9299
- filePath = join12(projectRoot, "app", "(auth)", route.slice(1), "page.tsx");
9158
+ filePath = join11(projectRoot, "app", "(auth)", route.slice(1), "page.tsx");
9300
9159
  } else {
9301
- filePath = join12(projectRoot, "app", route.slice(1), "page.tsx");
9160
+ filePath = join11(projectRoot, "app", route.slice(1), "page.tsx");
9302
9161
  }
9303
9162
  if (!existsSync19(filePath)) continue;
9304
- const code = readFileSync14(filePath, "utf-8");
9163
+ const code = readFileSync13(filePath, "utf-8");
9305
9164
  if (code.length < 50) continue;
9306
9165
  page.pageAnalysis = analyzePageCode(code);
9307
9166
  changed = true;
@@ -9328,7 +9187,7 @@ async function autoInstallShadcnComponent(componentId, projectRoot) {
9328
9187
  const def = getShadcnComponent(componentId);
9329
9188
  if (!def) return false;
9330
9189
  try {
9331
- const configPath = join12(projectRoot, "design-system.config.ts");
9190
+ const configPath = join11(projectRoot, "design-system.config.ts");
9332
9191
  let config2 = null;
9333
9192
  try {
9334
9193
  const mgr = new DesignSystemManager8(configPath);
@@ -9337,10 +9196,10 @@ async function autoInstallShadcnComponent(componentId, projectRoot) {
9337
9196
  }
9338
9197
  const generator = new ComponentGenerator3(config2 || { components: [], pages: [], tokens: {} });
9339
9198
  const code = await generator.generate(def);
9340
- const uiDir = join12(projectRoot, "components", "ui");
9199
+ const uiDir = join11(projectRoot, "components", "ui");
9341
9200
  const { mkdirSync: mkdirSync9 } = await import("fs");
9342
9201
  mkdirSync9(uiDir, { recursive: true });
9343
- writeFileSync10(join12(uiDir, `${componentId}.tsx`), code, "utf-8");
9202
+ writeFileSync10(join11(uiDir, `${componentId}.tsx`), code, "utf-8");
9344
9203
  return true;
9345
9204
  } catch {
9346
9205
  return false;
@@ -9463,12 +9322,12 @@ async function openBrowser(url) {
9463
9322
  }
9464
9323
  }
9465
9324
  function startDevServer(projectRoot) {
9466
- const packageJsonPath = resolve11(projectRoot, "package.json");
9325
+ const packageJsonPath = resolve10(projectRoot, "package.json");
9467
9326
  if (!existsSync19(packageJsonPath)) {
9468
9327
  throw new Error('package.json not found. Run "coherent init" first.');
9469
9328
  }
9470
- const hasPnpm = existsSync19(resolve11(projectRoot, "pnpm-lock.yaml"));
9471
- const hasNpm = existsSync19(resolve11(projectRoot, "package-lock.json"));
9329
+ const hasPnpm = existsSync19(resolve10(projectRoot, "pnpm-lock.yaml"));
9330
+ const hasNpm = existsSync19(resolve10(projectRoot, "package-lock.json"));
9472
9331
  const command = hasPnpm ? "pnpm" : hasNpm ? "npm" : "npx";
9473
9332
  const args = hasPnpm ? ["dev", "--turbo"] : hasNpm ? ["run", "dev", "--", "--turbo"] : ["next", "dev", "--turbo"];
9474
9333
  const child = spawn(command, args, {
@@ -9515,7 +9374,7 @@ async function previewCommand() {
9515
9374
  if (needsGlobalsFix(projectRoot)) {
9516
9375
  spinner.text = "Fixing globals.css...";
9517
9376
  try {
9518
- const dsm = new DesignSystemManager8(resolve11(projectRoot, "design-system.config.ts"));
9377
+ const dsm = new DesignSystemManager8(resolve10(projectRoot, "design-system.config.ts"));
9519
9378
  await dsm.load();
9520
9379
  const config2 = dsm.getConfig();
9521
9380
  fixGlobalsCss(projectRoot, config2);
@@ -9554,8 +9413,8 @@ async function previewCommand() {
9554
9413
  import chalk15 from "chalk";
9555
9414
  import ora4 from "ora";
9556
9415
  import { spawn as spawn2 } from "child_process";
9557
- import { existsSync as existsSync20, rmSync as rmSync4, readdirSync as readdirSync4 } from "fs";
9558
- import { resolve as resolve12, join as join13, dirname as dirname7 } from "path";
9416
+ import { existsSync as existsSync20, rmSync as rmSync4, readdirSync as readdirSync3 } from "fs";
9417
+ import { resolve as resolve11, join as join12, dirname as dirname7 } from "path";
9559
9418
  import { readdir as readdir3, readFile as readFile5, writeFile as writeFile4, mkdir as mkdir4, copyFile as copyFile2 } from "fs/promises";
9560
9419
  var COPY_EXCLUDE = /* @__PURE__ */ new Set([
9561
9420
  "node_modules",
@@ -9577,8 +9436,8 @@ async function copyDir(src, dest) {
9577
9436
  await mkdir4(dest, { recursive: true });
9578
9437
  const entries = await readdir3(src, { withFileTypes: true });
9579
9438
  for (const e of entries) {
9580
- const srcPath = join13(src, e.name);
9581
- const destPath = join13(dest, e.name);
9439
+ const srcPath = join12(src, e.name);
9440
+ const destPath = join12(dest, e.name);
9582
9441
  if (COPY_EXCLUDE.has(e.name)) continue;
9583
9442
  if (e.isDirectory()) {
9584
9443
  await copyDir(srcPath, destPath);
@@ -9589,16 +9448,16 @@ async function copyDir(src, dest) {
9589
9448
  }
9590
9449
  }
9591
9450
  function checkProjectInitialized2(projectRoot) {
9592
- return existsSync20(resolve12(projectRoot, "design-system.config.ts")) && existsSync20(resolve12(projectRoot, "package.json"));
9451
+ return existsSync20(resolve11(projectRoot, "design-system.config.ts")) && existsSync20(resolve11(projectRoot, "package.json"));
9593
9452
  }
9594
9453
  function getPackageManager2(projectRoot) {
9595
- if (existsSync20(resolve12(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
9596
- if (existsSync20(resolve12(projectRoot, "package-lock.json"))) return "npm";
9454
+ if (existsSync20(resolve11(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
9455
+ if (existsSync20(resolve11(projectRoot, "package-lock.json"))) return "npm";
9597
9456
  return "npx";
9598
9457
  }
9599
9458
  async function patchNextConfigForExport(outRoot) {
9600
9459
  for (const name of ["next.config.ts", "next.config.mjs", "next.config.js"]) {
9601
- const p = join13(outRoot, name);
9460
+ const p = join12(outRoot, name);
9602
9461
  if (!existsSync20(p)) continue;
9603
9462
  let content = await readFile5(p, "utf-8");
9604
9463
  if (content.includes("ignoreDuringBuilds")) return;
@@ -9614,9 +9473,9 @@ async function buildProduction(projectRoot) {
9614
9473
  const pm = getPackageManager2(projectRoot);
9615
9474
  const command = pm === "pnpm" ? "pnpm" : pm === "npm" ? "npm" : "npx";
9616
9475
  const args = pm === "npx" ? ["next", "build"] : ["run", "build"];
9617
- return new Promise((resolve17, reject) => {
9476
+ return new Promise((resolve16, reject) => {
9618
9477
  const child = spawn2(command, args, { cwd: projectRoot, stdio: "inherit", shell: true });
9619
- child.on("exit", (code) => code === 0 ? resolve17() : reject(new Error(`Build failed with exit code ${code}`)));
9478
+ child.on("exit", (code) => code === 0 ? resolve16() : reject(new Error(`Build failed with exit code ${code}`)));
9620
9479
  child.on("error", (error) => reject(new Error(`Failed to start build: ${error.message}`)));
9621
9480
  });
9622
9481
  }
@@ -9640,7 +9499,7 @@ EXPOSE 3000
9640
9499
  \`\`\`
9641
9500
  `;
9642
9501
  async function ensureReadmeDeploySection(outRoot) {
9643
- const readmePath = join13(outRoot, "README.md");
9502
+ const readmePath = join12(outRoot, "README.md");
9644
9503
  if (!existsSync20(readmePath)) return;
9645
9504
  try {
9646
9505
  let content = await readFile5(readmePath, "utf-8");
@@ -9660,22 +9519,22 @@ async function countPages(outRoot) {
9660
9519
  return;
9661
9520
  }
9662
9521
  for (const e of entries) {
9663
- const full = join13(dir, e.name);
9522
+ const full = join12(dir, e.name);
9664
9523
  if (e.isFile() && e.name === "page.tsx") n++;
9665
9524
  else if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "api") await walk(full);
9666
9525
  }
9667
9526
  }
9668
- const appDir = join13(outRoot, "app");
9527
+ const appDir = join12(outRoot, "app");
9669
9528
  if (existsSync20(appDir)) await walk(appDir);
9670
9529
  return n;
9671
9530
  }
9672
9531
  function countComponents(outRoot) {
9673
9532
  let n = 0;
9674
9533
  for (const sub of ["ui", "shared"]) {
9675
- const dir = join13(outRoot, "components", sub);
9534
+ const dir = join12(outRoot, "components", sub);
9676
9535
  if (!existsSync20(dir)) continue;
9677
9536
  try {
9678
- n += readdirSync4(dir).filter((f) => f.endsWith(".tsx") || f.endsWith(".jsx")).length;
9537
+ n += readdirSync3(dir).filter((f) => f.endsWith(".tsx") || f.endsWith(".jsx")).length;
9679
9538
  } catch {
9680
9539
  }
9681
9540
  }
@@ -9688,7 +9547,7 @@ async function collectImportedPackages2(dir, extensions) {
9688
9547
  async function walk(d) {
9689
9548
  const entries = await readdir3(d, { withFileTypes: true });
9690
9549
  for (const e of entries) {
9691
- const full = join13(d, e.name);
9550
+ const full = join12(d, e.name);
9692
9551
  if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules") {
9693
9552
  await walk(full);
9694
9553
  continue;
@@ -9711,7 +9570,7 @@ async function collectImportedPackages2(dir, extensions) {
9711
9570
  return packages;
9712
9571
  }
9713
9572
  async function findMissingDepsInExport(outRoot) {
9714
- const pkgPath = join13(outRoot, "package.json");
9573
+ const pkgPath = join12(outRoot, "package.json");
9715
9574
  if (!existsSync20(pkgPath)) return [];
9716
9575
  let pkg;
9717
9576
  try {
@@ -9720,7 +9579,7 @@ async function findMissingDepsInExport(outRoot) {
9720
9579
  return [];
9721
9580
  }
9722
9581
  const inDeps = /* @__PURE__ */ new Set([...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})]);
9723
- const codeDirs = [join13(outRoot, "app"), join13(outRoot, "components")];
9582
+ const codeDirs = [join12(outRoot, "app"), join12(outRoot, "components")];
9724
9583
  const extensions = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx"]);
9725
9584
  const imported = /* @__PURE__ */ new Set();
9726
9585
  for (const dir of codeDirs) {
@@ -9732,25 +9591,33 @@ async function findMissingDepsInExport(outRoot) {
9732
9591
  async function stripCoherentArtifacts(outputDir) {
9733
9592
  const removed = [];
9734
9593
  for (const p of ["app/design-system", "app/api/design-system"]) {
9735
- const full = join13(outputDir, p);
9594
+ const full = join12(outputDir, p);
9736
9595
  if (existsSync20(full)) {
9737
9596
  rmSync4(full, { recursive: true, force: true });
9738
9597
  removed.push(p);
9739
9598
  }
9740
9599
  }
9741
- const appNavPath = join13(outputDir, "app", "AppNav.tsx");
9600
+ const appNavPath = join12(outputDir, "app", "AppNav.tsx");
9742
9601
  if (existsSync20(appNavPath)) {
9743
9602
  rmSync4(appNavPath, { force: true });
9744
9603
  removed.push("app/AppNav.tsx");
9745
9604
  }
9746
- const layoutPath = join13(outputDir, "app", "layout.tsx");
9605
+ const layoutPath = join12(outputDir, "app", "layout.tsx");
9747
9606
  if (existsSync20(layoutPath)) {
9748
9607
  let layout = await readFile5(layoutPath, "utf-8");
9749
9608
  layout = layout.replace(/import\s*\{?\s*AppNav\s*\}?\s*from\s*['"][^'"]+['"]\s*\n?/g, "");
9750
9609
  layout = layout.replace(/\s*<AppNav\s*\/?\s*>\s*/g, "\n");
9751
9610
  await writeFile4(layoutPath, layout, "utf-8");
9752
9611
  }
9753
- const guardPath = join13(outputDir, "app", "ShowWhenNotAuthRoute.tsx");
9612
+ const sharedHeaderPath = join12(outputDir, "components", "shared", "header.tsx");
9613
+ if (existsSync20(sharedHeaderPath)) {
9614
+ let header = await readFile5(sharedHeaderPath, "utf-8");
9615
+ header = header.replace(/<Link\s[^>]*href="\/design-system"[^>]*>[\s\S]*?<\/Link>/g, "");
9616
+ header = header.replace(/\n\s*<>\s*\n/, "\n");
9617
+ header = header.replace(/\n\s*<\/>\s*\n/, "\n");
9618
+ await writeFile4(sharedHeaderPath, header, "utf-8");
9619
+ }
9620
+ const guardPath = join12(outputDir, "app", "ShowWhenNotAuthRoute.tsx");
9754
9621
  if (existsSync20(guardPath)) {
9755
9622
  let guard = await readFile5(guardPath, "utf-8");
9756
9623
  guard = guard.replace(/['"],?\s*'\/design-system['"],?\s*/g, "");
@@ -9779,14 +9646,14 @@ async function stripCoherentArtifacts(outputDir) {
9779
9646
  ".env.local",
9780
9647
  "recommendations.md"
9781
9648
  ]) {
9782
- const full = join13(outputDir, name);
9649
+ const full = join12(outputDir, name);
9783
9650
  if (existsSync20(full)) {
9784
9651
  rmSync4(full, { force: true });
9785
9652
  removed.push(name);
9786
9653
  }
9787
9654
  }
9788
9655
  for (const dir of [".claude", ".coherent"]) {
9789
- const full = join13(outputDir, dir);
9656
+ const full = join12(outputDir, dir);
9790
9657
  if (existsSync20(full)) {
9791
9658
  rmSync4(full, { recursive: true, force: true });
9792
9659
  removed.push(dir + "/");
@@ -9795,7 +9662,7 @@ async function stripCoherentArtifacts(outputDir) {
9795
9662
  return removed;
9796
9663
  }
9797
9664
  async function exportCommand(options = {}) {
9798
- const outputDir = resolve12(process.cwd(), options.output ?? "./export");
9665
+ const outputDir = resolve11(process.cwd(), options.output ?? "./export");
9799
9666
  const doBuild = options.build !== false;
9800
9667
  const keepDs = options.keepDs === true;
9801
9668
  const spinner = ora4("Preparing export...").start();
@@ -9986,8 +9853,8 @@ async function regenerateDocsCommand() {
9986
9853
 
9987
9854
  // src/commands/fix.ts
9988
9855
  import chalk18 from "chalk";
9989
- import { readdirSync as readdirSync5, readFileSync as readFileSync15, existsSync as existsSync21, writeFileSync as writeFileSync11, rmSync as rmSync5, mkdirSync as mkdirSync7 } from "fs";
9990
- import { resolve as resolve13, join as join14 } from "path";
9856
+ import { readdirSync as readdirSync4, readFileSync as readFileSync14, existsSync as existsSync21, writeFileSync as writeFileSync11, rmSync as rmSync5, mkdirSync as mkdirSync7 } from "fs";
9857
+ import { resolve as resolve12, join as join13 } from "path";
9991
9858
  import {
9992
9859
  DesignSystemManager as DesignSystemManager11,
9993
9860
  ComponentManager as ComponentManager5,
@@ -10011,9 +9878,9 @@ function extractComponentIdsFromCode2(code) {
10011
9878
  function listTsxFiles(dir) {
10012
9879
  const files = [];
10013
9880
  try {
10014
- const entries = readdirSync5(dir, { withFileTypes: true });
9881
+ const entries = readdirSync4(dir, { withFileTypes: true });
10015
9882
  for (const e of entries) {
10016
- const full = join14(dir, e.name);
9883
+ const full = join13(dir, e.name);
10017
9884
  if (e.isDirectory() && e.name !== "node_modules" && !e.name.startsWith(".")) {
10018
9885
  files.push(...listTsxFiles(full));
10019
9886
  } else if (e.isFile() && e.name.endsWith(".tsx")) {
@@ -10042,7 +9909,7 @@ async function fixCommand(opts = {}) {
10042
9909
  console.log(chalk18.cyan("\ncoherent fix\n"));
10043
9910
  }
10044
9911
  if (!skipCache) {
10045
- const nextDir = join14(projectRoot, ".next");
9912
+ const nextDir = join13(projectRoot, ".next");
10046
9913
  if (existsSync21(nextDir)) {
10047
9914
  if (!dryRun) rmSync5(nextDir, { recursive: true, force: true });
10048
9915
  fixes.push("Cleared build cache");
@@ -10065,12 +9932,12 @@ async function fixCommand(opts = {}) {
10065
9932
  }
10066
9933
  }
10067
9934
  }
10068
- const appDir = resolve13(projectRoot, "app");
9935
+ const appDir = resolve12(projectRoot, "app");
10069
9936
  const allTsxFiles = listTsxFiles(appDir);
10070
- const componentsTsxFiles = listTsxFiles(resolve13(projectRoot, "components"));
9937
+ const componentsTsxFiles = listTsxFiles(resolve12(projectRoot, "components"));
10071
9938
  const allComponentIds = /* @__PURE__ */ new Set();
10072
9939
  for (const file of [...allTsxFiles, ...componentsTsxFiles]) {
10073
- const content = readFileSync15(file, "utf-8");
9940
+ const content = readFileSync14(file, "utf-8");
10074
9941
  extractComponentIdsFromCode2(content).forEach((id) => allComponentIds.add(id));
10075
9942
  }
10076
9943
  let dsm = null;
@@ -10089,7 +9956,7 @@ async function fixCommand(opts = {}) {
10089
9956
  missingComponents.push(id);
10090
9957
  } else {
10091
9958
  const fileName = toKebabCase(id) + ".tsx";
10092
- const filePath = resolve13(projectRoot, "components", "ui", fileName);
9959
+ const filePath = resolve12(projectRoot, "components", "ui", fileName);
10093
9960
  if (!existsSync21(filePath)) missingFiles.push(id);
10094
9961
  }
10095
9962
  }
@@ -10117,8 +9984,8 @@ async function fixCommand(opts = {}) {
10117
9984
  const generator = new ComponentGenerator4(updatedConfig);
10118
9985
  const code = await generator.generate(component);
10119
9986
  const fileName = toKebabCase(component.name) + ".tsx";
10120
- const filePath = resolve13(projectRoot, "components", "ui", fileName);
10121
- mkdirSync7(resolve13(projectRoot, "components", "ui"), { recursive: true });
9987
+ const filePath = resolve12(projectRoot, "components", "ui", fileName);
9988
+ mkdirSync7(resolve12(projectRoot, "components", "ui"), { recursive: true });
10122
9989
  await writeFile(filePath, code);
10123
9990
  installed++;
10124
9991
  }
@@ -10139,7 +10006,7 @@ async function fixCommand(opts = {}) {
10139
10006
  const userTsxFiles = allTsxFiles.filter((f) => !f.includes("/design-system/"));
10140
10007
  let syntaxFixed = 0;
10141
10008
  for (const file of userTsxFiles) {
10142
- const content = readFileSync15(file, "utf-8");
10009
+ const content = readFileSync14(file, "utf-8");
10143
10010
  const fixed = fixUnescapedLtInJsx(
10144
10011
  fixEscapedClosingQuotes(sanitizeMetadataStrings(ensureUseClientIfNeeded(content)))
10145
10012
  );
@@ -10157,7 +10024,7 @@ async function fixCommand(opts = {}) {
10157
10024
  let qualityFixCount = 0;
10158
10025
  const qualityFixDetails = [];
10159
10026
  for (const file of userTsxFiles) {
10160
- const content = readFileSync15(file, "utf-8");
10027
+ const content = readFileSync14(file, "utf-8");
10161
10028
  const { code: autoFixed, fixes: fileFixes } = await autoFixCode(content);
10162
10029
  if (autoFixed !== content) {
10163
10030
  if (!dryRun) writeFileSync11(file, autoFixed, "utf-8");
@@ -10176,7 +10043,7 @@ async function fixCommand(opts = {}) {
10176
10043
  let totalWarnings = 0;
10177
10044
  const fileIssues = [];
10178
10045
  for (const file of allTsxFiles) {
10179
- const code = dryRun ? readFileSync15(file, "utf-8") : readFileSync15(file, "utf-8");
10046
+ const code = dryRun ? readFileSync14(file, "utf-8") : readFileSync14(file, "utf-8");
10180
10047
  const relativePath = file.replace(projectRoot + "/", "");
10181
10048
  const baseName = file.split("/").pop() || "";
10182
10049
  const isAuthPage = relativePath.includes("(auth)");
@@ -10298,16 +10165,16 @@ async function fixCommand(opts = {}) {
10298
10165
 
10299
10166
  // src/commands/check.ts
10300
10167
  import chalk19 from "chalk";
10301
- import { resolve as resolve14 } from "path";
10302
- import { readdirSync as readdirSync6, readFileSync as readFileSync16, statSync as statSync2, existsSync as existsSync22 } from "fs";
10168
+ import { resolve as resolve13 } from "path";
10169
+ import { readdirSync as readdirSync5, readFileSync as readFileSync15, statSync as statSync2, existsSync as existsSync22 } from "fs";
10303
10170
  import { loadManifest as loadManifest11 } from "@getcoherent/core";
10304
10171
  var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["node_modules", "design-system"]);
10305
10172
  function findTsxFiles(dir) {
10306
10173
  const results = [];
10307
10174
  try {
10308
- const entries = readdirSync6(dir);
10175
+ const entries = readdirSync5(dir);
10309
10176
  for (const entry of entries) {
10310
- const full = resolve14(dir, entry);
10177
+ const full = resolve13(dir, entry);
10311
10178
  const stat = statSync2(full);
10312
10179
  if (stat.isDirectory() && !entry.startsWith(".") && !EXCLUDED_DIRS.has(entry)) {
10313
10180
  results.push(...findTsxFiles(full));
@@ -10342,7 +10209,7 @@ async function checkCommand(opts = {}) {
10342
10209
  } catch {
10343
10210
  }
10344
10211
  if (!skipPages) {
10345
- const appDir = resolve14(projectRoot, "app");
10212
+ const appDir = resolve13(projectRoot, "app");
10346
10213
  const files = findTsxFiles(appDir);
10347
10214
  result.pages.total = files.length;
10348
10215
  if (!opts.json) console.log(chalk19.cyan("\n \u{1F4C4} Pages") + chalk19.dim(` (${files.length} scanned)
@@ -10356,7 +10223,7 @@ async function checkCommand(opts = {}) {
10356
10223
  "NATIVE_TABLE"
10357
10224
  ]);
10358
10225
  for (const file of files) {
10359
- const code = readFileSync16(file, "utf-8");
10226
+ const code = readFileSync15(file, "utf-8");
10360
10227
  const relativePath = file.replace(projectRoot + "/", "");
10361
10228
  const baseName = file.split("/").pop() || "";
10362
10229
  const isAuthPage = relativePath.includes("(auth)");
@@ -10398,7 +10265,7 @@ async function checkCommand(opts = {}) {
10398
10265
  routeSet.add("/");
10399
10266
  routeSet.add("#");
10400
10267
  for (const file of files) {
10401
- const code = readFileSync16(file, "utf-8");
10268
+ const code = readFileSync15(file, "utf-8");
10402
10269
  const relativePath = file.replace(projectRoot + "/", "");
10403
10270
  const lines = code.split("\n");
10404
10271
  const linkHrefRe = /href\s*=\s*["'](\/[a-z0-9/-]*)["']/gi;
@@ -10430,7 +10297,7 @@ async function checkCommand(opts = {}) {
10430
10297
  const manifest = await loadManifest11(project.root);
10431
10298
  if (manifest.shared.length > 0) {
10432
10299
  for (const entry of manifest.shared) {
10433
- const fullPath = resolve14(project.root, entry.file);
10300
+ const fullPath = resolve13(project.root, entry.file);
10434
10301
  if (!existsSync22(fullPath)) {
10435
10302
  result.pages.withErrors++;
10436
10303
  if (!opts.json) console.log(chalk19.red(`
@@ -10455,7 +10322,7 @@ async function checkCommand(opts = {}) {
10455
10322
  let _staleUsedIn = 0;
10456
10323
  let _nameMismatch = 0;
10457
10324
  for (const entry of manifest.shared) {
10458
- const filePath = resolve14(projectRoot, entry.file);
10325
+ const filePath = resolve13(projectRoot, entry.file);
10459
10326
  const fileExists = existsSync22(filePath);
10460
10327
  if (!fileExists) {
10461
10328
  _orphaned++;
@@ -10466,7 +10333,7 @@ async function checkCommand(opts = {}) {
10466
10333
  continue;
10467
10334
  }
10468
10335
  try {
10469
- const code = readFileSync16(filePath, "utf-8");
10336
+ const code = readFileSync15(filePath, "utf-8");
10470
10337
  const actualExports = extractExportedComponentNames(code);
10471
10338
  if (actualExports.length > 0 && !actualExports.includes(entry.name)) {
10472
10339
  _nameMismatch++;
@@ -10534,7 +10401,7 @@ async function checkCommand(opts = {}) {
10534
10401
  id: e.id,
10535
10402
  name: e.name,
10536
10403
  type: e.type,
10537
- status: existsSync22(resolve14(projectRoot, e.file)) ? "ok" : "unused",
10404
+ status: existsSync22(resolve13(projectRoot, e.file)) ? "ok" : "unused",
10538
10405
  message: "",
10539
10406
  suggestions: void 0
10540
10407
  }))
@@ -10627,14 +10494,14 @@ import {
10627
10494
  ComponentManager as ComponentManager6,
10628
10495
  loadManifest as loadManifest12,
10629
10496
  generateSharedComponent as generateSharedComponent4,
10630
- integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout4
10497
+ integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout3
10631
10498
  } from "@getcoherent/core";
10632
10499
  import { existsSync as existsSync23 } from "fs";
10633
- import { resolve as resolve15 } from "path";
10500
+ import { resolve as resolve14 } from "path";
10634
10501
 
10635
10502
  // src/utils/ds-files.ts
10636
10503
  import { mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
10637
- import { join as join15, dirname as dirname8 } from "path";
10504
+ import { join as join14, dirname as dirname8 } from "path";
10638
10505
  import { DesignSystemGenerator } from "@getcoherent/core";
10639
10506
  var SHARED_DS_KEYS = [
10640
10507
  "app/design-system/shared/page.tsx",
@@ -10648,7 +10515,7 @@ async function writeDesignSystemFiles(projectRoot, config2, options) {
10648
10515
  const toWrite = options?.sharedOnly ? new Map([...files].filter(([path3]) => SHARED_DS_KEYS.includes(path3))) : files;
10649
10516
  const written = [];
10650
10517
  for (const [relativePath, content] of toWrite) {
10651
- const fullPath = join15(projectRoot, relativePath);
10518
+ const fullPath = join14(projectRoot, relativePath);
10652
10519
  await mkdir5(dirname8(fullPath), { recursive: true });
10653
10520
  await writeFile5(fullPath, content, "utf-8");
10654
10521
  written.push(relativePath);
@@ -10757,10 +10624,10 @@ function createComponentsCommand() {
10757
10624
  \u2705 Created ${result.id} (${result.name}) at ${result.file}
10758
10625
  `));
10759
10626
  if (type === "layout") {
10760
- const updated = await integrateSharedLayoutIntoRootLayout4(project.root);
10627
+ const updated = await integrateSharedLayoutIntoRootLayout3(project.root);
10761
10628
  if (updated) console.log(chalk25.cyan(" Updated app/layout.tsx to use shared layout components.\n"));
10762
10629
  }
10763
- const sharedPagePath = resolve15(project.root, "app/design-system/shared/page.tsx");
10630
+ const sharedPagePath = resolve14(project.root, "app/design-system/shared/page.tsx");
10764
10631
  if (!existsSync23(sharedPagePath)) {
10765
10632
  try {
10766
10633
  const dsm = new DesignSystemManager12(project.configPath);
@@ -10787,7 +10654,7 @@ function createComponentsCommand() {
10787
10654
  import chalk26 from "chalk";
10788
10655
  import ora6 from "ora";
10789
10656
  import { writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
10790
- import { resolve as resolve16, join as join16, dirname as dirname9 } from "path";
10657
+ import { resolve as resolve15, join as join15, dirname as dirname9 } from "path";
10791
10658
  import { existsSync as existsSync24 } from "fs";
10792
10659
  import {
10793
10660
  FigmaClient,
@@ -10800,7 +10667,7 @@ import {
10800
10667
  setSharedMapping,
10801
10668
  generateSharedComponent as generateSharedComponent5,
10802
10669
  generatePagesFromFigma,
10803
- integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout5,
10670
+ integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout4,
10804
10671
  DesignSystemManager as DesignSystemManager13,
10805
10672
  validateConfig,
10806
10673
  FRAMEWORK_VERSIONS as FRAMEWORK_VERSIONS2,
@@ -10935,7 +10802,7 @@ async function importFigmaAction(urlOrKey, opts) {
10935
10802
  stats.filesWritten.push(filePath);
10936
10803
  return;
10937
10804
  }
10938
- const fullPath = join16(projectRoot, filePath);
10805
+ const fullPath = join15(projectRoot, filePath);
10939
10806
  await mkdir6(dirname9(fullPath), { recursive: true });
10940
10807
  await writeFile6(fullPath, content, "utf-8");
10941
10808
  stats.filesWritten.push(filePath);
@@ -11012,7 +10879,7 @@ async function importFigmaAction(urlOrKey, opts) {
11012
10879
  if (dryRun) stats.filesWritten.push(FIGMA_COMPONENT_MAP_FILENAME);
11013
10880
  else
11014
10881
  await writeFile6(
11015
- resolve16(projectRoot, FIGMA_COMPONENT_MAP_FILENAME),
10882
+ resolve15(projectRoot, FIGMA_COMPONENT_MAP_FILENAME),
11016
10883
  JSON.stringify(componentMapObj, null, 2),
11017
10884
  "utf-8"
11018
10885
  );
@@ -11035,7 +10902,7 @@ async function importFigmaAction(urlOrKey, opts) {
11035
10902
  const fullConfig = buildFigmaImportConfig(mergedConfig, pageDefs, intermediate.fileName);
11036
10903
  if (!dryRun) {
11037
10904
  spinner.start("Updating design-system.config.ts...");
11038
- const configPath = resolve16(projectRoot, DESIGN_SYSTEM_CONFIG_PATH);
10905
+ const configPath = resolve15(projectRoot, DESIGN_SYSTEM_CONFIG_PATH);
11039
10906
  const dsm = new DesignSystemManager13(configPath);
11040
10907
  if (existsSync24(configPath)) {
11041
10908
  await dsm.load();
@@ -11066,7 +10933,7 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
11066
10933
  stats.configUpdated = true;
11067
10934
  spinner.succeed("design-system.config.ts updated");
11068
10935
  spinner.start("Ensuring root layout...");
11069
- const layoutPath = join16(projectRoot, "app/layout.tsx");
10936
+ const layoutPath = join15(projectRoot, "app/layout.tsx");
11070
10937
  if (!existsSync24(layoutPath)) {
11071
10938
  await mkdir6(dirname9(layoutPath), { recursive: true });
11072
10939
  await writeFile6(layoutPath, MINIMAL_ROOT_LAYOUT, "utf-8");
@@ -11074,7 +10941,7 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
11074
10941
  }
11075
10942
  spinner.succeed("Root layout OK");
11076
10943
  spinner.start("Integrating shared layout (Header/Footer)...");
11077
- const layoutIntegrated = await integrateSharedLayoutIntoRootLayout5(projectRoot);
10944
+ const layoutIntegrated = await integrateSharedLayoutIntoRootLayout4(projectRoot);
11078
10945
  stats.layoutIntegrated = layoutIntegrated;
11079
10946
  spinner.succeed(layoutIntegrated ? "Layout components wired" : "No layout components to wire");
11080
10947
  spinner.start("Generating Design System viewer...");
@@ -11157,8 +11024,8 @@ async function dsRegenerateCommand() {
11157
11024
  // src/commands/update.ts
11158
11025
  import chalk28 from "chalk";
11159
11026
  import ora8 from "ora";
11160
- import { readFileSync as readFileSync17, existsSync as existsSync25 } from "fs";
11161
- import { join as join17 } from "path";
11027
+ import { readFileSync as readFileSync16, existsSync as existsSync25 } from "fs";
11028
+ import { join as join16 } from "path";
11162
11029
  import { DesignSystemManager as DesignSystemManager15, CLI_VERSION as CLI_VERSION4 } from "@getcoherent/core";
11163
11030
 
11164
11031
  // src/utils/migrations.ts
@@ -11327,20 +11194,20 @@ var EXPECTED_CSS_VARS = [
11327
11194
  "--sidebar-ring"
11328
11195
  ];
11329
11196
  function checkMissingCssVars(projectRoot) {
11330
- const globalsPath = join17(projectRoot, "app", "globals.css");
11197
+ const globalsPath = join16(projectRoot, "app", "globals.css");
11331
11198
  if (!existsSync25(globalsPath)) return [];
11332
11199
  try {
11333
- const content = readFileSync17(globalsPath, "utf-8");
11200
+ const content = readFileSync16(globalsPath, "utf-8");
11334
11201
  return EXPECTED_CSS_VARS.filter((v) => !content.includes(v));
11335
11202
  } catch {
11336
11203
  return [];
11337
11204
  }
11338
11205
  }
11339
11206
  function patchGlobalsCss(projectRoot, missingVars) {
11340
- const globalsPath = join17(projectRoot, "app", "globals.css");
11207
+ const globalsPath = join16(projectRoot, "app", "globals.css");
11341
11208
  if (!existsSync25(globalsPath) || missingVars.length === 0) return;
11342
11209
  const { writeFileSync: writeFileSync13 } = __require("fs");
11343
- let content = readFileSync17(globalsPath, "utf-8");
11210
+ let content = readFileSync16(globalsPath, "utf-8");
11344
11211
  const defaultValues = {
11345
11212
  "--chart-1": "220 70% 50%",
11346
11213
  "--chart-2": "160 60% 45%",
@@ -11418,26 +11285,26 @@ async function undoCommand(options) {
11418
11285
  // src/commands/sync.ts
11419
11286
  import chalk30 from "chalk";
11420
11287
  import ora9 from "ora";
11421
- import { existsSync as existsSync26, readFileSync as readFileSync18 } from "fs";
11422
- import { join as join18, relative as relative4, dirname as dirname10 } from "path";
11288
+ import { existsSync as existsSync26, readFileSync as readFileSync17 } from "fs";
11289
+ import { join as join17, relative as relative4, dirname as dirname10 } from "path";
11423
11290
  import { readdir as readdir4, readFile as readFile6 } from "fs/promises";
11424
11291
  import { DesignSystemManager as DesignSystemManager16 } from "@getcoherent/core";
11425
11292
  import { loadManifest as loadManifest13, saveManifest as saveManifest5, findSharedComponent } from "@getcoherent/core";
11426
11293
  function extractTokensFromProject(projectRoot) {
11427
11294
  const lightColors = {};
11428
11295
  const darkColors = {};
11429
- const globalsPath = join18(projectRoot, "app", "globals.css");
11296
+ const globalsPath = join17(projectRoot, "app", "globals.css");
11430
11297
  if (existsSync26(globalsPath)) {
11431
- const css = readFileSync18(globalsPath, "utf-8");
11298
+ const css = readFileSync17(globalsPath, "utf-8");
11432
11299
  const rootMatch = css.match(/:root\s*\{([^}]+)\}/s);
11433
11300
  if (rootMatch) parseVarsInto(rootMatch[1], lightColors);
11434
11301
  const darkMatch = css.match(/\.dark\s*\{([^}]+)\}/s);
11435
11302
  if (darkMatch) parseVarsInto(darkMatch[1], darkColors);
11436
11303
  }
11437
- const layoutPath = join18(projectRoot, "app", "layout.tsx");
11304
+ const layoutPath = join17(projectRoot, "app", "layout.tsx");
11438
11305
  let layoutCode = "";
11439
11306
  if (existsSync26(layoutPath)) {
11440
- layoutCode = readFileSync18(layoutPath, "utf-8");
11307
+ layoutCode = readFileSync17(layoutPath, "utf-8");
11441
11308
  const rootInline = layoutCode.match(/:root\s*\{([^}]+)\}/s);
11442
11309
  if (rootInline && Object.keys(lightColors).length === 0) {
11443
11310
  parseVarsInto(rootInline[1], lightColors);
@@ -11455,7 +11322,7 @@ function extractTokensFromProject(projectRoot) {
11455
11322
  defaultMode = "dark";
11456
11323
  }
11457
11324
  let radius;
11458
- const allCss = [existsSync26(globalsPath) ? readFileSync18(globalsPath, "utf-8") : "", layoutCode].join("\n");
11325
+ const allCss = [existsSync26(globalsPath) ? readFileSync17(globalsPath, "utf-8") : "", layoutCode].join("\n");
11459
11326
  const radiusMatch = allCss.match(/--radius:\s*([^;]+);/);
11460
11327
  if (radiusMatch) radius = radiusMatch[1].trim();
11461
11328
  return {
@@ -11478,7 +11345,7 @@ function parseVarsInto(block, target) {
11478
11345
  }
11479
11346
  async function detectCustomComponents(projectRoot, allPageCode) {
11480
11347
  const results = [];
11481
- const componentsDir = join18(projectRoot, "components");
11348
+ const componentsDir = join17(projectRoot, "components");
11482
11349
  if (!existsSync26(componentsDir)) return results;
11483
11350
  const files = [];
11484
11351
  await walkForTsx(componentsDir, files, ["ui"]);
@@ -11506,7 +11373,7 @@ async function walkForTsx(dir, files, skipDirs) {
11506
11373
  return;
11507
11374
  }
11508
11375
  for (const e of entries) {
11509
- const full = join18(dir, e.name);
11376
+ const full = join17(dir, e.name);
11510
11377
  if (e.isDirectory()) {
11511
11378
  if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
11512
11379
  await walkForTsx(full, files, skipDirs);
@@ -11580,7 +11447,7 @@ async function discoverPages(appDir) {
11580
11447
  return;
11581
11448
  }
11582
11449
  for (const entry of entries) {
11583
- const full = join18(dir, entry.name);
11450
+ const full = join17(dir, entry.name);
11584
11451
  if (entry.isDirectory()) {
11585
11452
  if (["design-system", "api", "_not-found"].includes(entry.name)) continue;
11586
11453
  if (entry.name.startsWith(".")) continue;
@@ -11657,7 +11524,7 @@ async function syncCommand(options = {}) {
11657
11524
  if (dryRun) console.log(chalk30.yellow(" [dry-run] No files will be written\n"));
11658
11525
  const spinner = ora9("Scanning project files...").start();
11659
11526
  try {
11660
- const appDir = join18(project.root, "app");
11527
+ const appDir = join17(project.root, "app");
11661
11528
  if (!existsSync26(appDir)) {
11662
11529
  spinner.fail("No app/ directory found");
11663
11530
  process.exit(1);
@@ -11883,20 +11750,20 @@ async function syncCommand(options = {}) {
11883
11750
  }
11884
11751
 
11885
11752
  // src/utils/update-notifier.ts
11886
- import { existsSync as existsSync27, mkdirSync as mkdirSync8, readFileSync as readFileSync19, writeFileSync as writeFileSync12 } from "fs";
11887
- import { join as join19 } from "path";
11753
+ import { existsSync as existsSync27, mkdirSync as mkdirSync8, readFileSync as readFileSync18, writeFileSync as writeFileSync12 } from "fs";
11754
+ import { join as join18 } from "path";
11888
11755
  import { homedir } from "os";
11889
11756
  import chalk31 from "chalk";
11890
11757
  import { CLI_VERSION as CLI_VERSION5 } from "@getcoherent/core";
11891
11758
  var DEBUG5 = process.env.COHERENT_DEBUG === "1";
11892
11759
  var PACKAGE_NAME = "@getcoherent/cli";
11893
- var CACHE_DIR = join19(homedir(), ".coherent");
11894
- var CACHE_FILE = join19(CACHE_DIR, "update-check.json");
11760
+ var CACHE_DIR = join18(homedir(), ".coherent");
11761
+ var CACHE_FILE = join18(CACHE_DIR, "update-check.json");
11895
11762
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
11896
11763
  function readCache() {
11897
11764
  try {
11898
11765
  if (!existsSync27(CACHE_FILE)) return null;
11899
- const raw = readFileSync19(CACHE_FILE, "utf-8");
11766
+ const raw = readFileSync18(CACHE_FILE, "utf-8");
11900
11767
  return JSON.parse(raw);
11901
11768
  } catch (e) {
11902
11769
  if (DEBUG5) console.error("Failed to read update cache:", e);