@getcoherent/cli 0.2.4 → 0.3.1

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 +362 -500
  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
  }
@@ -1019,8 +1019,8 @@ export default function HomePage() {
1019
1019
  </div>
1020
1020
  </section>
1021
1021
 
1022
- {/* Footer */}
1023
- <footer className="border-t pt-8 pb-8 mt-auto">
1022
+ {/* Footer links (shared Footer component provides the main footer) */}
1023
+ <div className="border-t pt-8 pb-8 mt-auto">
1024
1024
  <div className="flex flex-col items-center gap-5 px-4">
1025
1025
  <div className="flex items-center gap-3">
1026
1026
  <div className="flex size-8 items-center justify-center rounded-lg bg-primary text-primary-foreground shrink-0">
@@ -1028,7 +1028,7 @@ export default function HomePage() {
1028
1028
  </div>
1029
1029
  <span className="text-sm font-semibold">Coherent Design Method</span>
1030
1030
  </div>
1031
- <nav className="flex flex-wrap items-center justify-center gap-x-6 gap-y-2">
1031
+ <div className="flex flex-wrap items-center justify-center gap-x-6 gap-y-2">
1032
1032
  <a
1033
1033
  href="https://getcoherent.design"
1034
1034
  target="_blank"
@@ -1046,7 +1046,7 @@ export default function HomePage() {
1046
1046
  <a href="#" className="text-xs text-muted-foreground hover:text-foreground transition-colors">
1047
1047
  Privacy Policy
1048
1048
  </a>
1049
- </nav>
1049
+ </div>
1050
1050
  <p className="text-xs text-muted-foreground">
1051
1051
  \xA9 {new Date().getFullYear()}{' '}
1052
1052
  <a
@@ -1059,7 +1059,7 @@ export default function HomePage() {
1059
1059
  </a>
1060
1060
  </p>
1061
1061
  </div>
1062
- </footer>
1062
+ </div>
1063
1063
  </div>
1064
1064
  )
1065
1065
  }
@@ -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
@@ -6614,7 +6622,8 @@ import {
6614
6622
  PageGenerator,
6615
6623
  TailwindConfigGenerator
6616
6624
  } from "@getcoherent/core";
6617
- 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";
6618
6627
  async function validateAndFixGeneratedCode(projectRoot, code, options = {}) {
6619
6628
  const fixes = [];
6620
6629
  let fixed = fixEscapedClosingQuotes(code);
@@ -6684,18 +6693,49 @@ async function regenerateLayout(config2, projectRoot) {
6684
6693
  const layout = config2.pages[0]?.layout || "centered";
6685
6694
  const appType = config2.settings.appType || "multi-page";
6686
6695
  const generator = new PageGenerator(config2);
6687
- const code = await generator.generateLayout(layout, appType);
6688
- const layoutPath = resolve6(projectRoot, "app", "layout.tsx");
6689
- await writeFile(layoutPath, code);
6690
- if (config2.navigation?.enabled && appType === "multi-page") {
6691
- const appNavCode = generator.generateAppNav();
6692
- const appNavPath = resolve6(projectRoot, "app", "AppNav.tsx");
6693
- 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
+ }
6694
6731
  }
6695
6732
  try {
6696
6733
  await integrateSharedLayoutIntoRootLayout2(projectRoot);
6697
6734
  await ensureAuthRouteGroup(projectRoot);
6698
- } catch {
6735
+ } catch (err) {
6736
+ if (process.env.COHERENT_DEBUG === "1") {
6737
+ console.log(chalk9.dim("Layout integration warning:", err));
6738
+ }
6699
6739
  }
6700
6740
  }
6701
6741
  async function regenerateFiles(modified, config2, projectRoot) {
@@ -6733,8 +6773,39 @@ async function regenerateFiles(modified, config2, projectRoot) {
6733
6773
  }
6734
6774
  }
6735
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
+
6736
6807
  // src/commands/chat/reporting.ts
6737
- import chalk9 from "chalk";
6808
+ import chalk10 from "chalk";
6738
6809
  function extractImportsFrom(code, fromPath) {
6739
6810
  const escaped = fromPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6740
6811
  const regex = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*['"\`]${escaped}[^'"\`]*['"\`]`, "g");
@@ -6756,27 +6827,27 @@ function printPostGenerationReport(opts) {
6756
6827
  const iconCount = extractImportsFrom(code, "lucide-react").length;
6757
6828
  const hasInstalled = postFixes.some((f) => f.startsWith("Installed:"));
6758
6829
  const syntaxStatus = postFixes.length > 0 ? postFixes.some((f) => f.includes("metadata")) ? "fixed (escaped metadata quotes) \u2714" : "fixed \u2714" : "valid \u2714";
6759
- console.log(chalk9.green(`
6830
+ console.log(chalk10.green(`
6760
6831
  \u2705 Page "${pageTitle}" ${action} at ${filePath}
6761
6832
  `));
6762
6833
  if (uiComponents.length > 0) {
6763
- console.log(chalk9.dim(` Components: ${uiComponents.join(", ")} (from @/components/ui)`));
6834
+ console.log(chalk10.dim(` Components: ${uiComponents.join(", ")} (from @/components/ui)`));
6764
6835
  }
6765
6836
  if (inCodeShared.length > 0) {
6766
- 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(", ")}`));
6767
6838
  }
6768
6839
  if (layoutShared.length > 0) {
6769
- 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`));
6770
6841
  }
6771
6842
  if (iconCount > 0) {
6772
- console.log(chalk9.dim(` Icons: ${iconCount} from lucide-react`));
6843
+ console.log(chalk10.dim(` Icons: ${iconCount} from lucide-react`));
6773
6844
  }
6774
6845
  if (hasInstalled) {
6775
- console.log(chalk9.dim(" Dependencies: installed \u2714"));
6846
+ console.log(chalk10.dim(" Dependencies: installed \u2714"));
6776
6847
  }
6777
- console.log(chalk9.dim(` Syntax: ${syntaxStatus}`));
6848
+ console.log(chalk10.dim(` Syntax: ${syntaxStatus}`));
6778
6849
  if (route) {
6779
- console.log(chalk9.cyan(`
6850
+ console.log(chalk10.cyan(`
6780
6851
  Preview: http://localhost:3000${route}`));
6781
6852
  }
6782
6853
  console.log("");
@@ -6784,35 +6855,35 @@ function printPostGenerationReport(opts) {
6784
6855
  function printSharedComponentReport(opts) {
6785
6856
  const { id, name, file, instruction, postFixes = [] } = opts;
6786
6857
  const syntaxStatus = postFixes.length > 0 ? "fixed \u2714" : "valid \u2714";
6787
- console.log(chalk9.green(`
6858
+ console.log(chalk10.green(`
6788
6859
  \u2705 Updated ${id} (${name}) at ${file}
6789
6860
  `));
6790
6861
  if (instruction) {
6791
6862
  const snippet = instruction.length > 60 ? instruction.slice(0, 57) + "..." : instruction;
6792
- console.log(chalk9.dim(` Changed: ${snippet}`));
6863
+ console.log(chalk10.dim(` Changed: ${snippet}`));
6793
6864
  }
6794
- console.log(chalk9.dim(" Affects: all pages via layout.tsx"));
6795
- console.log(chalk9.dim(` Syntax: ${syntaxStatus}`));
6865
+ console.log(chalk10.dim(" Affects: all pages via layout.tsx"));
6866
+ console.log(chalk10.dim(` Syntax: ${syntaxStatus}`));
6796
6867
  console.log("");
6797
6868
  }
6798
6869
  function printLinkSharedReport(opts) {
6799
6870
  const { sharedId, sharedName, pageTarget, route, postFixes = [] } = opts;
6800
6871
  const syntaxStatus = postFixes.length > 0 ? "fixed \u2714" : "valid \u2714";
6801
- console.log(chalk9.green(`
6872
+ console.log(chalk10.green(`
6802
6873
  \u2705 Linked ${sharedId} (${sharedName}) to page "${pageTarget}"
6803
6874
  `));
6804
- console.log(chalk9.dim(` Syntax: ${syntaxStatus}`));
6805
- 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}`));
6806
6877
  console.log("");
6807
6878
  }
6808
6879
  function printPromoteAndLinkReport(opts) {
6809
6880
  const { id, name, file, usedInFiles, postFixes = [] } = opts;
6810
6881
  const syntaxStatus = postFixes.length > 0 ? "fixed \u2714" : "valid \u2714";
6811
- console.log(chalk9.green(`
6882
+ console.log(chalk10.green(`
6812
6883
  \u2705 Created ${id} (${name}) at ${file}
6813
6884
  `));
6814
- console.log(chalk9.dim(` Linked to: ${usedInFiles.length} page(s)`));
6815
- console.log(chalk9.dim(` Syntax: ${syntaxStatus}`));
6885
+ console.log(chalk10.dim(` Linked to: ${usedInFiles.length} page(s)`));
6886
+ console.log(chalk10.dim(` Syntax: ${syntaxStatus}`));
6816
6887
  console.log("");
6817
6888
  }
6818
6889
  function showPreview(requests, results, config2, preflightInstalledNames) {
@@ -6830,23 +6901,23 @@ function showPreview(requests, results, config2, preflightInstalledNames) {
6830
6901
  const modifiedSharedComponents = successfulPairs.filter(({ request }) => request.type === "modify-layout-block");
6831
6902
  const modifiedPages = successfulPairs.filter(({ request }) => request.type === "update-page");
6832
6903
  const tokenChanges = successfulPairs.filter(({ request }) => request.type === "update-token");
6833
- console.log(chalk9.bold.cyan("\n\u{1F4CB} Changes Applied:\n"));
6904
+ console.log(chalk10.bold.cyan("\n\u{1F4CB} Changes Applied:\n"));
6834
6905
  if (preflightInstalledNames && preflightInstalledNames.length > 0) {
6835
- 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:"));
6836
6907
  preflightInstalledNames.forEach((name) => {
6837
- console.log(chalk9.green(` \u2728 Auto-installed ${name}`));
6908
+ console.log(chalk10.green(` \u2728 Auto-installed ${name}`));
6838
6909
  });
6839
6910
  console.log("");
6840
6911
  }
6841
6912
  if (addedComponents.length > 0) {
6842
6913
  const names = addedComponents.map(({ request }) => request.changes.name).filter(Boolean);
6843
- console.log(chalk9.green("\u{1F4E6} Components:"));
6844
- 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(", ")}`));
6845
6916
  }
6846
6917
  if (customComponents.length > 0) {
6847
6918
  const names = customComponents.map(({ request }) => request.changes.name).filter(Boolean);
6848
- if (addedComponents.length === 0) console.log(chalk9.green("\u{1F4E6} Components:"));
6849
- 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(", ")}`));
6850
6921
  }
6851
6922
  const usedComponentIds = /* @__PURE__ */ new Set();
6852
6923
  addedPages.forEach(({ request }) => {
@@ -6861,71 +6932,71 @@ function showPreview(requests, results, config2, preflightInstalledNames) {
6861
6932
  ]);
6862
6933
  const reusedIds = [...usedComponentIds].filter((id) => !newComponentIds.has(id));
6863
6934
  if (reusedIds.length > 0) {
6864
- if (addedComponents.length === 0 && customComponents.length === 0) console.log(chalk9.green("\u{1F4E6} Components:"));
6865
- 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(", ")}`));
6866
6937
  }
6867
6938
  if (addedComponents.length > 0 || customComponents.length > 0 || reusedIds.length > 0) {
6868
6939
  console.log("");
6869
6940
  }
6870
6941
  if (addedPages.length > 0) {
6871
- console.log(chalk9.green("\u{1F4C4} Pages Created:"));
6942
+ console.log(chalk10.green("\u{1F4C4} Pages Created:"));
6872
6943
  addedPages.forEach(({ request }) => {
6873
6944
  const page = request.changes;
6874
6945
  const route = page.route || "/";
6875
- console.log(chalk9.white(` \u2728 ${page.name || "Page"}`));
6876
- console.log(chalk9.gray(` Route: ${route}`));
6877
- 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}`));
6878
6949
  });
6879
6950
  console.log("");
6880
6951
  }
6881
6952
  if (modifiedComponents.length > 0 || modifiedSharedComponents.length > 0 || modifiedPages.length > 0 || tokenChanges.length > 0) {
6882
- console.log(chalk9.yellow("\u{1F527} Modified:"));
6953
+ console.log(chalk10.yellow("\u{1F527} Modified:"));
6883
6954
  modifiedComponents.forEach(({ result }) => {
6884
- console.log(chalk9.white(` \u2022 ${result.message}`));
6955
+ console.log(chalk10.white(` \u2022 ${result.message}`));
6885
6956
  });
6886
6957
  modifiedSharedComponents.forEach(({ result }) => {
6887
- console.log(chalk9.white(` \u2022 ${result.message}`));
6958
+ console.log(chalk10.white(` \u2022 ${result.message}`));
6888
6959
  });
6889
6960
  modifiedPages.forEach(({ result }) => {
6890
- console.log(chalk9.white(` \u2022 ${result.message}`));
6961
+ console.log(chalk10.white(` \u2022 ${result.message}`));
6891
6962
  });
6892
6963
  tokenChanges.forEach(({ result }) => {
6893
- console.log(chalk9.white(` \u2022 ${result.message}`));
6964
+ console.log(chalk10.white(` \u2022 ${result.message}`));
6894
6965
  });
6895
6966
  console.log("");
6896
6967
  }
6897
6968
  if (failedPairs.length > 0) {
6898
- console.log(chalk9.red("\u274C Failed modifications:"));
6969
+ console.log(chalk10.red("\u274C Failed modifications:"));
6899
6970
  failedPairs.forEach(({ result }) => {
6900
- console.log(chalk9.gray(` \u2716 ${result.message}`));
6971
+ console.log(chalk10.gray(` \u2716 ${result.message}`));
6901
6972
  });
6902
6973
  console.log("");
6903
6974
  }
6904
6975
  const successCount = successfulPairs.length;
6905
6976
  const totalCount = results.length;
6906
6977
  if (successCount === totalCount) {
6907
- console.log(chalk9.green.bold(`\u2705 Success! ${successCount} modification(s) applied
6978
+ console.log(chalk10.green.bold(`\u2705 Success! ${successCount} modification(s) applied
6908
6979
  `));
6909
6980
  } else {
6910
- 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
6911
6982
  `));
6912
6983
  }
6913
6984
  if (addedPages.length > 0) {
6914
6985
  const firstPage = addedPages[0].request.changes;
6915
6986
  const route = firstPage?.route || "/";
6916
- console.log(chalk9.cyan("\u{1F680} What's next:\n"));
6917
- console.log(chalk9.white(" \u{1F4FA} View in browser:"));
6918
- console.log(chalk9.cyan(" coherent preview"));
6919
- 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}
6920
6991
  `));
6921
- console.log(chalk9.white(" \u{1F3A8} Customize:"));
6922
- console.log(chalk9.cyan(' coherent chat "make buttons rounded"'));
6923
- 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"}"`));
6924
6995
  console.log("");
6925
6996
  } else if (successCount > 0) {
6926
- console.log(chalk9.cyan("\u{1F680} What's next:\n"));
6927
- console.log(chalk9.white(" \u{1F4FA} Preview changes:"));
6928
- 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"));
6929
7000
  }
6930
7001
  }
6931
7002
  function getChangeDescription(request, config2) {
@@ -6967,6 +7038,24 @@ function getChangeDescription(request, config2) {
6967
7038
 
6968
7039
  // src/commands/chat/modification-handler.ts
6969
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
+ }
6970
7059
  async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider, originalMessage) {
6971
7060
  switch (request.type) {
6972
7061
  case "modify-layout-block": {
@@ -7005,8 +7094,8 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7005
7094
  const newCode = await ai.editSharedComponentCode(currentCode, instruction, resolved.name);
7006
7095
  const { fixedCode, fixes } = await validateAndFixGeneratedCode(projectRoot, newCode, { isPage: false });
7007
7096
  if (fixes.length > 0) {
7008
- console.log(chalk10.dim(" \u{1F527} Post-generation fixes:"));
7009
- 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}`)));
7010
7099
  }
7011
7100
  await writeFile(fullPath, fixedCode);
7012
7101
  printSharedComponentReport({
@@ -7079,11 +7168,11 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7079
7168
  const newPageCode = await ai.replaceInlineWithShared(pageCode, sharedCode, resolved.name, changes?.blockHint);
7080
7169
  const { fixedCode, fixes } = await validateAndFixGeneratedCode(projectRoot, newPageCode, { isPage: true });
7081
7170
  if (fixes.length > 0) {
7082
- console.log(chalk10.dim(" \u{1F527} Post-generation fixes:"));
7083
- 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}`)));
7084
7173
  }
7085
7174
  await writeFile(pageFilePath, fixedCode);
7086
- const manifest = await loadManifest5(projectRoot);
7175
+ const manifest = await loadManifest6(projectRoot);
7087
7176
  const usedIn = manifest.shared.find((e) => e.id === resolved.id)?.usedIn ?? [];
7088
7177
  const routePath = route.replace(/^\//, "");
7089
7178
  const filePathRel = routePath ? `app/${routePath}/page.tsx` : "app/page.tsx";
@@ -7153,7 +7242,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7153
7242
  };
7154
7243
  }
7155
7244
  const extractedCode = await ai.extractBlockAsComponent(sourceCode, blockHint, componentName);
7156
- const created = await generateSharedComponent2(projectRoot, {
7245
+ const created = await generateSharedComponent3(projectRoot, {
7157
7246
  name: componentName,
7158
7247
  type: "section",
7159
7248
  code: extractedCode,
@@ -7182,13 +7271,13 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7182
7271
  const newCode = await ai.replaceInlineWithShared(linkPageCode, sharedCode, created.name, blockHint);
7183
7272
  const { fixedCode, fixes } = await validateAndFixGeneratedCode(projectRoot, newCode, { isPage: true });
7184
7273
  if (fixes.length > 0) {
7185
- console.log(chalk10.dim(" \u{1F527} Post-generation fixes:"));
7186
- 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}`)));
7187
7276
  }
7188
7277
  await writeFile(fullPath, fixedCode);
7189
7278
  usedInFiles.push(relPath);
7190
7279
  }
7191
- const manifest = await loadManifest5(projectRoot);
7280
+ const manifest = await loadManifest6(projectRoot);
7192
7281
  const nextManifest = updateUsedIn(manifest, created.id, usedInFiles);
7193
7282
  await saveManifest(projectRoot, nextManifest);
7194
7283
  printPromoteAndLinkReport({
@@ -7277,7 +7366,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7277
7366
  const aiPageCode = typeof page.pageCode === "string" && page.pageCode.trim() !== "" ? page.pageCode : void 0;
7278
7367
  if (aiPageCode) {
7279
7368
  finalPageCode = aiPageCode;
7280
- 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)`));
7281
7370
  } else if (page.pageType && page.structuredContent) {
7282
7371
  const templateFn = getTemplateForPageType(page.pageType);
7283
7372
  if (templateFn) {
@@ -7288,9 +7377,9 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7288
7377
  pageName
7289
7378
  };
7290
7379
  finalPageCode = templateFn(page.structuredContent, opts);
7291
- 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)`));
7292
7381
  } catch {
7293
- if (DEBUG2) console.log(chalk10.dim(` [template] Failed for "${page.pageType}"`));
7382
+ if (DEBUG2) console.log(chalk11.dim(` [template] Failed for "${page.pageType}"`));
7294
7383
  }
7295
7384
  }
7296
7385
  }
@@ -7333,10 +7422,13 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7333
7422
  let codeToWrite = fixedCode;
7334
7423
  const { code: autoFixed, fixes: autoFixes } = await autoFixCode(codeToWrite);
7335
7424
  codeToWrite = autoFixed;
7425
+ const { code: layoutStripped, stripped } = stripInlineLayoutElements(codeToWrite);
7426
+ codeToWrite = layoutStripped;
7336
7427
  const allFixes = [...postFixes, ...autoFixes];
7428
+ if (stripped.length > 0) allFixes.push(`stripped inline ${stripped.join(", ")} (layout owns these)`);
7337
7429
  if (allFixes.length > 0) {
7338
- console.log(chalk10.dim(" \u{1F527} Post-generation fixes:"));
7339
- 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}`)));
7340
7432
  }
7341
7433
  await writeFile(filePath, codeToWrite);
7342
7434
  const pageIdx = dsm.getConfig().pages.findIndex((p) => p.id === page.id);
@@ -7347,7 +7439,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7347
7439
  cm.updateConfig(cfg);
7348
7440
  pm.updateConfig(cfg);
7349
7441
  }
7350
- const manifestForAudit = await loadManifest5(projectRoot);
7442
+ const manifestForAudit = await loadManifest6(projectRoot);
7351
7443
  await warnInlineDuplicates(projectRoot, page.name || page.id || route.slice(1), codeToWrite, manifestForAudit);
7352
7444
  const relFilePath = routeToRelPath(route, isAuth);
7353
7445
  printPostGenerationReport({
@@ -7366,7 +7458,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7366
7458
  const errors = issues.filter((i) => i.severity === "error");
7367
7459
  if (errors.length >= 5 && aiProvider) {
7368
7460
  console.log(
7369
- chalk10.yellow(`
7461
+ chalk11.yellow(`
7370
7462
  \u{1F504} ${errors.length} quality errors \u2014 attempting AI fix for ${page.name || page.id}...`)
7371
7463
  );
7372
7464
  try {
@@ -7388,7 +7480,7 @@ Rules:
7388
7480
  if (recheckErrors.length < errors.length) {
7389
7481
  codeToWrite = fixedCode2;
7390
7482
  await writeFile(filePath, codeToWrite);
7391
- 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`));
7392
7484
  }
7393
7485
  }
7394
7486
  }
@@ -7397,9 +7489,9 @@ Rules:
7397
7489
  }
7398
7490
  const report = formatIssues(issues);
7399
7491
  if (report) {
7400
- console.log(chalk10.yellow(`
7492
+ console.log(chalk11.yellow(`
7401
7493
  \u{1F50D} Quality check for ${page.name || page.id}:`));
7402
- console.log(chalk10.dim(report));
7494
+ console.log(chalk11.dim(report));
7403
7495
  }
7404
7496
  }
7405
7497
  }
@@ -7414,9 +7506,9 @@ Rules:
7414
7506
  const changes = request.changes;
7415
7507
  const instruction = originalMessage || (typeof changes?.instruction === "string" ? changes.instruction : void 0);
7416
7508
  let resolvedPageCode = typeof changes?.pageCode === "string" && changes.pageCode.trim() !== "" ? changes.pageCode : void 0;
7417
- 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)}...`));
7418
7510
  if (DEBUG2 && resolvedPageCode)
7419
- console.log(chalk10.dim(` [update-page] has pageCode (${resolvedPageCode.length} chars)`));
7511
+ console.log(chalk11.dim(` [update-page] has pageCode (${resolvedPageCode.length} chars)`));
7420
7512
  const configChanges = { ...changes };
7421
7513
  delete configChanges.pageCode;
7422
7514
  delete configChanges.pageType;
@@ -7440,12 +7532,12 @@ Rules:
7440
7532
  try {
7441
7533
  currentCode = await readFile(absPath);
7442
7534
  } catch {
7443
- 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}`));
7444
7536
  }
7445
7537
  if (currentCode) {
7446
7538
  const ai = await createAIProvider(aiProvider ?? "auto");
7447
7539
  if (ai.editPageCode) {
7448
- console.log(chalk10.dim(" \u270F\uFE0F Applying changes to existing page..."));
7540
+ console.log(chalk11.dim(" \u270F\uFE0F Applying changes to existing page..."));
7449
7541
  const coreRules = CORE_CONSTRAINTS;
7450
7542
  const qualityRules = DESIGN_QUALITY;
7451
7543
  const contextualRules = selectContextualRules(instruction);
@@ -7465,9 +7557,9 @@ ${contextualRules}
7465
7557
  ${routeRules}
7466
7558
  ${pagesCtx}`
7467
7559
  );
7468
- 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`));
7469
7561
  } else {
7470
- console.log(chalk10.yellow(" \u26A0 AI provider does not support editPageCode"));
7562
+ console.log(chalk11.yellow(" \u26A0 AI provider does not support editPageCode"));
7471
7563
  }
7472
7564
  }
7473
7565
  }
@@ -7497,10 +7589,13 @@ ${pagesCtx}`
7497
7589
  let codeToWrite = fixedCode;
7498
7590
  const { code: autoFixed, fixes: autoFixes } = await autoFixCode(codeToWrite);
7499
7591
  codeToWrite = autoFixed;
7592
+ const { code: layoutStripped, stripped } = stripInlineLayoutElements(codeToWrite);
7593
+ codeToWrite = layoutStripped;
7500
7594
  const allFixes = [...postFixes, ...autoFixes];
7595
+ if (stripped.length > 0) allFixes.push(`stripped inline ${stripped.join(", ")} (layout owns these)`);
7501
7596
  if (allFixes.length > 0) {
7502
- console.log(chalk10.dim(" \u{1F527} Post-generation fixes:"));
7503
- 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}`)));
7504
7599
  }
7505
7600
  await writeFile(absPath, codeToWrite);
7506
7601
  const updatePageIdx = dsm.getConfig().pages.findIndex((p) => p.id === pageDef.id);
@@ -7511,7 +7606,7 @@ ${pagesCtx}`
7511
7606
  cm.updateConfig(cfg);
7512
7607
  pm.updateConfig(cfg);
7513
7608
  }
7514
- const manifestForAudit = await loadManifest5(projectRoot);
7609
+ const manifestForAudit = await loadManifest6(projectRoot);
7515
7610
  await warnInlineDuplicates(
7516
7611
  projectRoot,
7517
7612
  pageDef.name || pageDef.id || route.slice(1),
@@ -7533,9 +7628,9 @@ ${pagesCtx}`
7533
7628
  const issues = validatePageQuality(codeToWrite);
7534
7629
  const report = formatIssues(issues);
7535
7630
  if (report) {
7536
- console.log(chalk10.yellow(`
7631
+ console.log(chalk11.yellow(`
7537
7632
  \u{1F50D} Quality check for ${pageDef.name || pageDef.id}:`));
7538
- console.log(chalk10.dim(report));
7633
+ console.log(chalk11.dim(report));
7539
7634
  }
7540
7635
  } else {
7541
7636
  try {
@@ -7544,11 +7639,11 @@ ${pagesCtx}`
7544
7639
  if (fixes.length > 0) {
7545
7640
  code = fixed;
7546
7641
  await writeFile(absPath, code);
7547
- console.log(chalk10.dim(" \u{1F527} Auto-fixes applied:"));
7548
- 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}`)));
7549
7644
  }
7550
7645
  const relFilePath = routeToRelPath(route, isAuth);
7551
- const manifest = await loadManifest5(projectRoot);
7646
+ const manifest = await loadManifest6(projectRoot);
7552
7647
  printPostGenerationReport({
7553
7648
  action: "updated",
7554
7649
  pageTitle: pageDef.name || pageDef.id || "Page",
@@ -7562,9 +7657,9 @@ ${pagesCtx}`
7562
7657
  const issues = validatePageQuality(code);
7563
7658
  const report = formatIssues(issues);
7564
7659
  if (report) {
7565
- console.log(chalk10.yellow(`
7660
+ console.log(chalk11.yellow(`
7566
7661
  \u{1F50D} Quality check for ${pageDef.name || pageDef.id}:`));
7567
- console.log(chalk10.dim(report));
7662
+ console.log(chalk11.dim(report));
7568
7663
  }
7569
7664
  } catch {
7570
7665
  }
@@ -7593,236 +7688,10 @@ ${pagesCtx}`
7593
7688
  }
7594
7689
  }
7595
7690
 
7596
- // src/commands/chat/layout-extractor.ts
7597
- import { readFileSync as readFileSync9, readdirSync as readdirSync2 } from "fs";
7598
- import { join as join9, resolve as resolve8 } from "path";
7599
- import chalk11 from "chalk";
7600
- import { loadManifest as loadManifest6, generateSharedComponent as generateSharedComponent3, integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout3 } from "@getcoherent/core";
7601
-
7602
- // src/commands/chat/jsx-extractor.ts
7603
- function extractBalancedTag(source, tagName) {
7604
- const openRe = new RegExp(`<${tagName}\\b`, "gi");
7605
- const match = openRe.exec(source);
7606
- if (!match) return null;
7607
- const startIdx = match.index;
7608
- const openTagRe = new RegExp(`<${tagName}\\b`, "gi");
7609
- const closeTagRe = new RegExp(`</${tagName}>`, "gi");
7610
- const events = [];
7611
- let m;
7612
- openTagRe.lastIndex = startIdx;
7613
- while ((m = openTagRe.exec(source)) !== null) {
7614
- events.push({ pos: m.index, type: "open", end: m.index + m[0].length });
7615
- }
7616
- closeTagRe.lastIndex = startIdx;
7617
- while ((m = closeTagRe.exec(source)) !== null) {
7618
- events.push({ pos: m.index, type: "close", end: m.index + m[0].length });
7619
- }
7620
- events.sort((a, b) => a.pos - b.pos);
7621
- let depth = 0;
7622
- for (const ev of events) {
7623
- if (ev.pos < startIdx) continue;
7624
- if (ev.type === "open") depth++;
7625
- else {
7626
- depth--;
7627
- if (depth === 0) return source.slice(startIdx, ev.end);
7628
- }
7629
- }
7630
- return null;
7631
- }
7632
- function extractRelevantImports(fullSource, jsxBlock) {
7633
- const importLines = [];
7634
- const importRe = /^import\s+.*from\s+['"][^'"]+['"];?\s*$/gm;
7635
- let m;
7636
- while ((m = importRe.exec(fullSource)) !== null) {
7637
- const line = m[0];
7638
- const namesMatch = line.match(/import\s*\{([^}]+)\}/);
7639
- if (namesMatch) {
7640
- const names = namesMatch[1].split(",").map(
7641
- (n) => n.trim().split(/\s+as\s+/).pop().trim()
7642
- );
7643
- if (names.some((name) => jsxBlock.includes(name))) {
7644
- importLines.push(line);
7645
- }
7646
- }
7647
- const defaultMatch = line.match(/import\s+(\w+)\s+from/);
7648
- if (defaultMatch && jsxBlock.includes(defaultMatch[1])) {
7649
- importLines.push(line);
7650
- }
7651
- }
7652
- return [...new Set(importLines)];
7653
- }
7654
- function extractStateHooks(fullSource, jsxBlock) {
7655
- const hooks = [];
7656
- const stateRe = /const\s+\[(\w+),\s*(\w+)\]\s*=\s*useState\b[^)]*\)/g;
7657
- let m;
7658
- while ((m = stateRe.exec(fullSource)) !== null) {
7659
- const [fullMatch, getter, setter] = m;
7660
- if (jsxBlock.includes(getter) || jsxBlock.includes(setter)) {
7661
- hooks.push(fullMatch);
7662
- }
7663
- }
7664
- return hooks;
7665
- }
7666
- function addActiveNavToHeader(code) {
7667
- let result = code;
7668
- if (!result.includes("usePathname")) {
7669
- if (result.includes("from 'next/navigation'")) {
7670
- result = result.replace(
7671
- /import\s*\{([^}]+)\}\s*from\s*'next\/navigation'/,
7672
- (_, names) => `import { ${names.trim()}, usePathname } from 'next/navigation'`
7673
- );
7674
- } else {
7675
- result = result.replace(
7676
- "export function Header()",
7677
- "import { usePathname } from 'next/navigation'\n\nexport function Header()"
7678
- );
7679
- }
7680
- }
7681
- if (!result.includes("const pathname")) {
7682
- result = result.replace(
7683
- /export function Header\(\)\s*\{/,
7684
- "export function Header() {\n const pathname = usePathname()"
7685
- );
7686
- }
7687
- result = result.replace(
7688
- /<Link\s+href="(\/[^"]*?)"\s+className="([^"]*?)(?:text-foreground|text-muted-foreground(?:\s+hover:text-foreground)?(?:\s+transition-colors)?)([^"]*?)">/g,
7689
- (_, href, before, after) => {
7690
- const base = before.trim();
7691
- const trail = after.trim();
7692
- const staticParts = [base, trail].filter(Boolean).join(" ");
7693
- const space = staticParts ? " " : "";
7694
- return `<Link href="${href}" className={\`${staticParts}${space}\${pathname === '${href}' ? 'text-foreground font-medium' : 'text-muted-foreground hover:text-foreground transition-colors'}\`}>`;
7695
- }
7696
- );
7697
- return result;
7698
- }
7699
-
7700
- // src/commands/chat/layout-extractor.ts
7701
- function findAllPageFiles(dir) {
7702
- const results = [];
7703
- try {
7704
- for (const entry of readdirSync2(dir, { withFileTypes: true })) {
7705
- const full = join9(dir, entry.name);
7706
- if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".next" && entry.name !== "design-system") {
7707
- results.push(...findAllPageFiles(full));
7708
- } else if (entry.name === "page.tsx" || entry.name === "page.jsx") {
7709
- results.push(full);
7710
- }
7711
- }
7712
- } catch {
7713
- }
7714
- return results;
7715
- }
7716
- async function extractAndShareLayoutComponents(projectRoot, generatedPageFiles) {
7717
- const manifest = await loadManifest6(projectRoot);
7718
- const hasSharedHeader = manifest.shared.some((c) => c.type === "layout" && /header|nav/i.test(c.name));
7719
- const hasSharedFooter = manifest.shared.some((c) => c.type === "layout" && /footer/i.test(c.name));
7720
- if (hasSharedHeader && hasSharedFooter) return false;
7721
- let sourceCode = "";
7722
- for (const file of generatedPageFiles) {
7723
- try {
7724
- const code = readFileSync9(file, "utf-8");
7725
- if (code.includes("<header") || code.includes("<footer") || code.includes("<nav")) {
7726
- sourceCode = code;
7727
- break;
7728
- }
7729
- } catch {
7730
- continue;
7731
- }
7732
- }
7733
- if (!sourceCode) return false;
7734
- let extracted = false;
7735
- if (!hasSharedHeader) {
7736
- let headerJsx = extractBalancedTag(sourceCode, "header");
7737
- if (!headerJsx) headerJsx = extractBalancedTag(sourceCode, "nav");
7738
- if (headerJsx) {
7739
- const imports = extractRelevantImports(sourceCode, headerJsx);
7740
- const importBlock = imports.length > 0 ? imports.join("\n") + "\n" : "import Link from 'next/link'\n";
7741
- const stateHooks = extractStateHooks(sourceCode, headerJsx);
7742
- const needsReactImport = stateHooks.length > 0 && !importBlock.includes("from 'react'");
7743
- const reactImport = needsReactImport ? "import { useState } from 'react'\n" : "";
7744
- const stateBlock = stateHooks.length > 0 ? " " + stateHooks.join("\n ") + "\n" : "";
7745
- const returnIndent = stateBlock ? " " : " ";
7746
- let headerComponent = `'use client'
7747
-
7748
- ${reactImport}${importBlock}
7749
- export function Header() {
7750
- ${stateBlock}${returnIndent}return (
7751
- ${headerJsx}
7752
- )
7753
- }
7754
- `;
7755
- headerComponent = addActiveNavToHeader(headerComponent);
7756
- await generateSharedComponent3(projectRoot, {
7757
- name: "Header",
7758
- type: "layout",
7759
- code: headerComponent,
7760
- description: "Main site header/navigation",
7761
- usedIn: ["app/layout.tsx"]
7762
- });
7763
- extracted = true;
7764
- }
7765
- }
7766
- if (!hasSharedFooter) {
7767
- const footerJsx = extractBalancedTag(sourceCode, "footer");
7768
- if (footerJsx) {
7769
- const imports = extractRelevantImports(sourceCode, footerJsx);
7770
- const importBlock = imports.length > 0 ? imports.join("\n") + "\n" : "import Link from 'next/link'\n";
7771
- const stateHooks = extractStateHooks(sourceCode, footerJsx);
7772
- const needsReactImport = stateHooks.length > 0 && !importBlock.includes("from 'react'");
7773
- const reactImport = needsReactImport ? "import { useState } from 'react'\n" : "";
7774
- const stateBlock = stateHooks.length > 0 ? " " + stateHooks.join("\n ") + "\n" : "";
7775
- const returnIndent = stateBlock ? " " : " ";
7776
- const footerComponent = `'use client'
7777
-
7778
- ${reactImport}${importBlock}
7779
- export function Footer() {
7780
- ${stateBlock}${returnIndent}return (
7781
- ${footerJsx}
7782
- )
7783
- }
7784
- `;
7785
- await generateSharedComponent3(projectRoot, {
7786
- name: "Footer",
7787
- type: "layout",
7788
- code: footerComponent,
7789
- description: "Site footer",
7790
- usedIn: ["app/layout.tsx"]
7791
- });
7792
- extracted = true;
7793
- }
7794
- }
7795
- if (!extracted) return false;
7796
- await integrateSharedLayoutIntoRootLayout3(projectRoot);
7797
- await ensureAuthRouteGroup(projectRoot);
7798
- const allPageFiles = /* @__PURE__ */ new Set([...generatedPageFiles, ...findAllPageFiles(resolve8(projectRoot, "app"))]);
7799
- for (const file of allPageFiles) {
7800
- try {
7801
- let code = await readFile(file);
7802
- const original = code;
7803
- const headerBlock = extractBalancedTag(code, "header");
7804
- if (headerBlock) {
7805
- code = code.replace(headerBlock, "");
7806
- } else {
7807
- const navBlock = extractBalancedTag(code, "nav");
7808
- if (navBlock) code = code.replace(navBlock, "");
7809
- }
7810
- const footerBlock = extractBalancedTag(code, "footer");
7811
- if (footerBlock) code = code.replace(footerBlock, "");
7812
- code = code.replace(/\n{3,}/g, "\n\n");
7813
- if (code !== original) await writeFile(file, code);
7814
- } catch {
7815
- continue;
7816
- }
7817
- }
7818
- console.log(chalk11.cyan(" \u{1F517} Extracted Header and Footer as shared components (all pages via layout)"));
7819
- return true;
7820
- }
7821
-
7822
7691
  // src/commands/chat/interactive.ts
7823
7692
  import chalk12 from "chalk";
7824
- import { resolve as resolve9 } from "path";
7825
- 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";
7826
7695
  import { DesignSystemManager as DesignSystemManager6, ComponentManager as ComponentManager3, loadManifest as loadManifest7 } from "@getcoherent/core";
7827
7696
  var DEBUG3 = process.env.COHERENT_DEBUG === "1";
7828
7697
  async function interactiveChat(options, chatCommandFn) {
@@ -7842,13 +7711,13 @@ async function interactiveChat(options, chatCommandFn) {
7842
7711
  \u274C Invalid provider: ${options.provider}`));
7843
7712
  process.exit(1);
7844
7713
  }
7845
- const historyDir = resolve9(homedir2(), ".coherent");
7846
- const historyFile = resolve9(historyDir, "history");
7714
+ const historyDir = resolve8(homedir2(), ".coherent");
7715
+ const historyFile = resolve8(historyDir, "history");
7847
7716
  let history = [];
7848
7717
  try {
7849
7718
  mkdirSync5(historyDir, { recursive: true });
7850
7719
  if (existsSync15(historyFile)) {
7851
- history = readFileSync10(historyFile, "utf-8").split("\n").filter(Boolean).slice(-200);
7720
+ history = readFileSync9(historyFile, "utf-8").split("\n").filter(Boolean).slice(-200);
7852
7721
  }
7853
7722
  } catch (e) {
7854
7723
  if (DEBUG3) console.error("Failed to load REPL history:", e);
@@ -8072,7 +7941,7 @@ async function chatCommand(message, options) {
8072
7941
  }
8073
7942
  if (/switch to light mode|default to light|make.*light.*(default|theme)|light theme/i.test(message)) {
8074
7943
  spinner.start("Setting default theme to light...");
8075
- const layoutPath = resolve10(projectRoot, "app/layout.tsx");
7944
+ const layoutPath = resolve9(projectRoot, "app/layout.tsx");
8076
7945
  try {
8077
7946
  let layout = await readFile(layoutPath);
8078
7947
  layout = layout.replace(/className="dark"/, "");
@@ -8111,7 +7980,7 @@ async function chatCommand(message, options) {
8111
7980
  spinner.start("Parsing your request...");
8112
7981
  let manifest = await loadManifest8(project.root);
8113
7982
  const validShared = manifest.shared.filter((s) => {
8114
- const fp = resolve10(project.root, s.file);
7983
+ const fp = resolve9(project.root, s.file);
8115
7984
  return existsSync16(fp);
8116
7985
  });
8117
7986
  if (validShared.length !== manifest.shared.length) {
@@ -8407,21 +8276,6 @@ async function chatCommand(message, options) {
8407
8276
  const result = await applyModification(request, dsm, cm, pm, projectRoot, provider, message);
8408
8277
  results.push(result);
8409
8278
  }
8410
- const anyPageGenerated = normalizedRequests.some(
8411
- (req, i) => (req.type === "add-page" || req.type === "update-page") && results[i]?.success
8412
- );
8413
- if (anyPageGenerated) {
8414
- const generatedPageFiles = normalizedRequests.filter((req, i) => (req.type === "add-page" || req.type === "update-page") && results[i]?.success).map((req) => {
8415
- const page = req.changes;
8416
- const route = page.route || `/${page.id || "page"}`;
8417
- return routeToFsPath(projectRoot, route, isAuthRoute(route));
8418
- }).filter((f) => existsSync16(f));
8419
- try {
8420
- await extractAndShareLayoutComponents(projectRoot, generatedPageFiles);
8421
- } catch (err) {
8422
- if (DEBUG4) console.log(chalk13.dim("Shared layout extraction failed:", err));
8423
- }
8424
- }
8425
8279
  const currentConfig = dsm.getConfig();
8426
8280
  const autoScaffoldEnabled = currentConfig.settings.autoScaffold === true;
8427
8281
  const scaffoldedPages = [];
@@ -8435,7 +8289,7 @@ async function chatCommand(message, options) {
8435
8289
  let pageCode = "";
8436
8290
  if (existsSync16(pageFilePath)) {
8437
8291
  try {
8438
- pageCode = readFileSync11(pageFilePath, "utf-8");
8292
+ pageCode = readFileSync10(pageFilePath, "utf-8");
8439
8293
  } catch {
8440
8294
  }
8441
8295
  }
@@ -8512,7 +8366,7 @@ async function chatCommand(message, options) {
8512
8366
  const isAuth = isAuthRoute(linkedRoute);
8513
8367
  const filePath = routeToFsPath(projectRoot, linkedRoute, isAuth);
8514
8368
  if (isAuth) await ensureAuthRouteGroup(projectRoot);
8515
- const dir = resolve10(filePath, "..");
8369
+ const dir = resolve9(filePath, "..");
8516
8370
  if (!existsSync16(dir)) {
8517
8371
  mkdirSync6(dir, { recursive: true });
8518
8372
  }
@@ -8548,7 +8402,7 @@ async function chatCommand(message, options) {
8548
8402
  dsm.updateConfig(latestConfig);
8549
8403
  if (DEBUG4) console.log(chalk13.dim(` [theme] Set defaultMode to "${targetMode}"`));
8550
8404
  }
8551
- const layoutPath = resolve10(projectRoot, "app", "layout.tsx");
8405
+ const layoutPath = resolve9(projectRoot, "app", "layout.tsx");
8552
8406
  try {
8553
8407
  let layoutCode = await readFile(layoutPath);
8554
8408
  if (targetMode === "dark" && !layoutCode.includes('className="dark"')) {
@@ -8603,7 +8457,7 @@ async function chatCommand(message, options) {
8603
8457
  console.log("");
8604
8458
  }
8605
8459
  if (uxRecommendations) {
8606
- const recPath = resolve10(projectRoot, "recommendations.md");
8460
+ const recPath = resolve9(projectRoot, "recommendations.md");
8607
8461
  const section = `
8608
8462
 
8609
8463
  ---
@@ -8687,19 +8541,19 @@ ${uxRecommendations}
8687
8541
  import chalk14 from "chalk";
8688
8542
  import ora3 from "ora";
8689
8543
  import { spawn } from "child_process";
8690
- import { existsSync as existsSync19, rmSync as rmSync3, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
8691
- 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";
8692
8546
  import { readdir as readdir2 } from "fs/promises";
8693
8547
  import { DesignSystemManager as DesignSystemManager8, ComponentGenerator as ComponentGenerator3 } from "@getcoherent/core";
8694
8548
 
8695
8549
  // src/utils/file-watcher.ts
8696
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, existsSync as existsSync18 } from "fs";
8697
- 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";
8698
8552
  import { loadManifest as loadManifest9, saveManifest as saveManifest3 } from "@getcoherent/core";
8699
8553
 
8700
8554
  // src/utils/component-integrity.ts
8701
- import { existsSync as existsSync17, readFileSync as readFileSync12, readdirSync as readdirSync3 } from "fs";
8702
- 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";
8703
8557
  function extractExportedComponentNames(code) {
8704
8558
  const names = [];
8705
8559
  let m;
@@ -8725,14 +8579,14 @@ function arraysEqual(a, b) {
8725
8579
  }
8726
8580
  function findPagesImporting(projectRoot, componentName, componentFile) {
8727
8581
  const results = [];
8728
- const appDir = join10(projectRoot, "app");
8582
+ const appDir = join9(projectRoot, "app");
8729
8583
  if (!existsSync17(appDir)) return results;
8730
8584
  const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
8731
8585
  const componentImportPath = componentFile.replace(/\.tsx$/, "").replace(/\.jsx$/, "");
8732
8586
  for (const absPath of pageFiles) {
8733
8587
  if (absPath.includes("design-system")) continue;
8734
8588
  try {
8735
- const code = readFileSync12(absPath, "utf-8");
8589
+ const code = readFileSync11(absPath, "utf-8");
8736
8590
  const hasNamedImport = new RegExp(`import\\s+\\{[^}]*\\b${componentName}\\b[^}]*\\}\\s+from\\s+['"]`).test(code);
8737
8591
  const hasDefaultImport = new RegExp(`import\\s+${componentName}\\s+from\\s+['"]`).test(code);
8738
8592
  const hasPathImport = code.includes(`@/${componentImportPath}`);
@@ -8745,10 +8599,10 @@ function findPagesImporting(projectRoot, componentName, componentFile) {
8745
8599
  return results;
8746
8600
  }
8747
8601
  function isUsedInLayout(projectRoot, componentName) {
8748
- const layoutPath = join10(projectRoot, "app", "layout.tsx");
8602
+ const layoutPath = join9(projectRoot, "app", "layout.tsx");
8749
8603
  if (!existsSync17(layoutPath)) return false;
8750
8604
  try {
8751
- const code = readFileSync12(layoutPath, "utf-8");
8605
+ const code = readFileSync11(layoutPath, "utf-8");
8752
8606
  return code.includes(componentName);
8753
8607
  } catch {
8754
8608
  return false;
@@ -8756,7 +8610,7 @@ function isUsedInLayout(projectRoot, componentName) {
8756
8610
  }
8757
8611
  function findUnregisteredComponents(projectRoot, manifest) {
8758
8612
  const results = [];
8759
- const componentsDir = join10(projectRoot, "components");
8613
+ const componentsDir = join9(projectRoot, "components");
8760
8614
  if (!existsSync17(componentsDir)) return results;
8761
8615
  const registeredFiles = new Set(manifest.shared.map((s) => s.file));
8762
8616
  const registeredNames = new Set(manifest.shared.map((s) => s.name));
@@ -8769,7 +8623,7 @@ function findUnregisteredComponents(projectRoot, manifest) {
8769
8623
  const relFile = relative2(projectRoot, absPath);
8770
8624
  if (registeredFiles.has(relFile)) continue;
8771
8625
  try {
8772
- const code = readFileSync12(absPath, "utf-8");
8626
+ const code = readFileSync11(absPath, "utf-8");
8773
8627
  const exports = extractExportedComponentNames(code);
8774
8628
  for (const name of exports) {
8775
8629
  if (registeredNames.has(name)) continue;
@@ -8784,14 +8638,14 @@ function findUnregisteredComponents(projectRoot, manifest) {
8784
8638
  }
8785
8639
  function findInlineDuplicates(projectRoot, manifest) {
8786
8640
  const results = [];
8787
- const appDir = join10(projectRoot, "app");
8641
+ const appDir = join9(projectRoot, "app");
8788
8642
  if (!existsSync17(appDir)) return results;
8789
8643
  const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
8790
8644
  for (const absPath of pageFiles) {
8791
8645
  if (absPath.includes("design-system")) continue;
8792
8646
  let code;
8793
8647
  try {
8794
- code = readFileSync12(absPath, "utf-8");
8648
+ code = readFileSync11(absPath, "utf-8");
8795
8649
  } catch {
8796
8650
  continue;
8797
8651
  }
@@ -8814,7 +8668,7 @@ function findInlineDuplicates(projectRoot, manifest) {
8814
8668
  return results;
8815
8669
  }
8816
8670
  function findComponentFileByExportName(projectRoot, componentName) {
8817
- const componentsDir = join10(projectRoot, "components");
8671
+ const componentsDir = join9(projectRoot, "components");
8818
8672
  if (!existsSync17(componentsDir)) return null;
8819
8673
  const files = collectFiles(
8820
8674
  componentsDir,
@@ -8823,7 +8677,7 @@ function findComponentFileByExportName(projectRoot, componentName) {
8823
8677
  );
8824
8678
  for (const absPath of files) {
8825
8679
  try {
8826
- const code = readFileSync12(absPath, "utf-8");
8680
+ const code = readFileSync11(absPath, "utf-8");
8827
8681
  const exports = extractExportedComponentNames(code);
8828
8682
  if (exports.includes(componentName)) {
8829
8683
  return relative2(projectRoot, absPath);
@@ -8836,7 +8690,7 @@ function findComponentFileByExportName(projectRoot, componentName) {
8836
8690
  function removeOrphanedEntries(projectRoot, manifest) {
8837
8691
  const removed = [];
8838
8692
  const valid = manifest.shared.filter((entry) => {
8839
- const filePath = join10(projectRoot, entry.file);
8693
+ const filePath = join9(projectRoot, entry.file);
8840
8694
  if (existsSync17(filePath)) return true;
8841
8695
  removed.push({ id: entry.id, name: entry.name });
8842
8696
  return false;
@@ -8855,7 +8709,7 @@ function reconcileComponents(projectRoot, manifest) {
8855
8709
  };
8856
8710
  const m = { ...manifest, shared: [...manifest.shared], nextId: manifest.nextId };
8857
8711
  m.shared = m.shared.filter((entry) => {
8858
- const filePath = join10(projectRoot, entry.file);
8712
+ const filePath = join9(projectRoot, entry.file);
8859
8713
  if (!existsSync17(filePath)) {
8860
8714
  const newPath = findComponentFileByExportName(projectRoot, entry.name);
8861
8715
  if (newPath) {
@@ -8868,7 +8722,7 @@ function reconcileComponents(projectRoot, manifest) {
8868
8722
  }
8869
8723
  let code;
8870
8724
  try {
8871
- code = readFileSync12(join10(projectRoot, entry.file), "utf-8");
8725
+ code = readFileSync11(join9(projectRoot, entry.file), "utf-8");
8872
8726
  } catch {
8873
8727
  return true;
8874
8728
  }
@@ -8945,12 +8799,12 @@ function collectFiles(dir, filter, skipDirs = []) {
8945
8799
  function walk(d) {
8946
8800
  let entries;
8947
8801
  try {
8948
- entries = readdirSync3(d, { withFileTypes: true });
8802
+ entries = readdirSync2(d, { withFileTypes: true });
8949
8803
  } catch {
8950
8804
  return;
8951
8805
  }
8952
8806
  for (const e of entries) {
8953
- const full = join10(d, e.name);
8807
+ const full = join9(d, e.name);
8954
8808
  if (e.isDirectory()) {
8955
8809
  if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
8956
8810
  walk(full);
@@ -8989,9 +8843,9 @@ function findInlineDuplicatesOfShared(content, manifest) {
8989
8843
  }
8990
8844
  function getWatcherConfig(projectRoot) {
8991
8845
  try {
8992
- const pkgPath = join11(projectRoot, "package.json");
8846
+ const pkgPath = join10(projectRoot, "package.json");
8993
8847
  if (!existsSync18(pkgPath)) return defaultWatcherConfig();
8994
- const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
8848
+ const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
8995
8849
  const c = pkg?.coherent?.watcher ?? {};
8996
8850
  return {
8997
8851
  enabled: c.enabled !== false,
@@ -9019,7 +8873,7 @@ async function handleFileChange(projectRoot, filePath) {
9019
8873
  if (relativePath.includes("node_modules") || relativePath.includes(".next")) return;
9020
8874
  let content;
9021
8875
  try {
9022
- content = readFileSync13(filePath, "utf-8");
8876
+ content = readFileSync12(filePath, "utf-8");
9023
8877
  } catch {
9024
8878
  return;
9025
8879
  }
@@ -9092,7 +8946,7 @@ async function detectNewComponent(projectRoot, filePath) {
9092
8946
  const manifest = await loadManifest9(projectRoot);
9093
8947
  const alreadyRegistered = manifest.shared.some((s) => s.file === relativePath);
9094
8948
  if (alreadyRegistered) return;
9095
- const code = readFileSync13(filePath, "utf-8");
8949
+ const code = readFileSync12(filePath, "utf-8");
9096
8950
  const exports = extractExportedComponentNames(code);
9097
8951
  if (exports.length > 0) {
9098
8952
  const alreadyByName = exports.every((n) => manifest.shared.some((s) => s.name === n));
@@ -9118,8 +8972,8 @@ function startFileWatcher(projectRoot) {
9118
8972
  let watcher = null;
9119
8973
  let manifestWatcher = null;
9120
8974
  import("chokidar").then((chokidar) => {
9121
- const appGlob = join11(projectRoot, "app", "**", "*.tsx");
9122
- const compGlob = join11(projectRoot, "components", "**", "*.tsx");
8975
+ const appGlob = join10(projectRoot, "app", "**", "*.tsx");
8976
+ const compGlob = join10(projectRoot, "components", "**", "*.tsx");
9123
8977
  watcher = chokidar.default.watch([appGlob, compGlob], {
9124
8978
  ignoreInitial: true,
9125
8979
  awaitWriteFinish: { stabilityThreshold: 500 }
@@ -9131,7 +8985,7 @@ function startFileWatcher(projectRoot) {
9131
8985
  });
9132
8986
  watcher.on("unlink", (fp) => handleFileDelete(projectRoot, fp));
9133
8987
  });
9134
- const manifestPath = join11(projectRoot, "coherent.components.json");
8988
+ const manifestPath = join10(projectRoot, "coherent.components.json");
9135
8989
  if (existsSync18(manifestPath)) {
9136
8990
  import("chokidar").then((chokidar) => {
9137
8991
  manifestWatcher = chokidar.default.watch(manifestPath, { ignoreInitial: true });
@@ -9146,7 +9000,7 @@ function startFileWatcher(projectRoot) {
9146
9000
 
9147
9001
  // src/commands/preview.ts
9148
9002
  function getPackageManager(projectRoot) {
9149
- const hasPnpm = existsSync19(resolve11(projectRoot, "pnpm-lock.yaml"));
9003
+ const hasPnpm = existsSync19(resolve10(projectRoot, "pnpm-lock.yaml"));
9150
9004
  return hasPnpm ? "pnpm" : "npm";
9151
9005
  }
9152
9006
  function runInstall(projectRoot) {
@@ -9168,8 +9022,8 @@ function runInstall(projectRoot) {
9168
9022
  });
9169
9023
  }
9170
9024
  function checkProjectInitialized(projectRoot) {
9171
- const configPath = resolve11(projectRoot, "design-system.config.ts");
9172
- const packageJsonPath = resolve11(projectRoot, "package.json");
9025
+ const configPath = resolve10(projectRoot, "design-system.config.ts");
9026
+ const packageJsonPath = resolve10(projectRoot, "package.json");
9173
9027
  if (!existsSync19(configPath)) {
9174
9028
  return false;
9175
9029
  }
@@ -9179,11 +9033,11 @@ function checkProjectInitialized(projectRoot) {
9179
9033
  return true;
9180
9034
  }
9181
9035
  function checkDependenciesInstalled(projectRoot) {
9182
- const nodeModulesPath = resolve11(projectRoot, "node_modules");
9036
+ const nodeModulesPath = resolve10(projectRoot, "node_modules");
9183
9037
  return existsSync19(nodeModulesPath);
9184
9038
  }
9185
9039
  function clearStaleCache(projectRoot) {
9186
- const nextDir = join12(projectRoot, ".next");
9040
+ const nextDir = join11(projectRoot, ".next");
9187
9041
  if (existsSync19(nextDir)) {
9188
9042
  rmSync3(nextDir, { recursive: true, force: true });
9189
9043
  console.log(chalk14.dim(" \u2714 Cleared stale build cache"));
@@ -9204,7 +9058,7 @@ async function listPageFiles(appDir) {
9204
9058
  async function walk(dir) {
9205
9059
  const entries = await readdir2(dir, { withFileTypes: true });
9206
9060
  for (const e of entries) {
9207
- const full = join12(dir, e.name);
9061
+ const full = join11(dir, e.name);
9208
9062
  if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "api" && e.name !== "design-system") await walk(full);
9209
9063
  else if (e.isFile() && e.name === "page.tsx") out.push(full);
9210
9064
  }
@@ -9213,10 +9067,10 @@ async function listPageFiles(appDir) {
9213
9067
  return out;
9214
9068
  }
9215
9069
  async function validateSyntax(projectRoot) {
9216
- const appDir = join12(projectRoot, "app");
9070
+ const appDir = join11(projectRoot, "app");
9217
9071
  const pages = await listPageFiles(appDir);
9218
9072
  for (const file of pages) {
9219
- const content = readFileSync14(file, "utf-8");
9073
+ const content = readFileSync13(file, "utf-8");
9220
9074
  const fixed = fixUnescapedLtInJsx(sanitizeMetadataStrings(ensureUseClientIfNeeded(content)));
9221
9075
  if (fixed !== content) {
9222
9076
  writeFileSync10(file, fixed, "utf-8");
@@ -9225,13 +9079,13 @@ async function validateSyntax(projectRoot) {
9225
9079
  }
9226
9080
  }
9227
9081
  async function fixMissingComponentExports(projectRoot) {
9228
- const appDir = join12(projectRoot, "app");
9229
- const uiDir = join12(projectRoot, "components", "ui");
9082
+ const appDir = join11(projectRoot, "app");
9083
+ const uiDir = join11(projectRoot, "components", "ui");
9230
9084
  if (!existsSync19(appDir) || !existsSync19(uiDir)) return;
9231
9085
  const pages = await listPageFiles(appDir);
9232
9086
  const neededExports = /* @__PURE__ */ new Map();
9233
9087
  for (const file of pages) {
9234
- const content = readFileSync14(file, "utf-8");
9088
+ const content = readFileSync13(file, "utf-8");
9235
9089
  const importRe = /import\s*\{([^}]+)\}\s*from\s*['"]@\/components\/ui\/([^'"]+)['"]/g;
9236
9090
  let m;
9237
9091
  while ((m = importRe.exec(content)) !== null) {
@@ -9241,7 +9095,7 @@ async function fixMissingComponentExports(projectRoot) {
9241
9095
  for (const name of names) neededExports.get(componentId).add(name);
9242
9096
  }
9243
9097
  }
9244
- const configPath = join12(projectRoot, "design-system.config.ts");
9098
+ const configPath = join11(projectRoot, "design-system.config.ts");
9245
9099
  let config2 = null;
9246
9100
  try {
9247
9101
  const mgr = new DesignSystemManager8(configPath);
@@ -9250,7 +9104,7 @@ async function fixMissingComponentExports(projectRoot) {
9250
9104
  }
9251
9105
  const generator = new ComponentGenerator3(config2 || { components: [], pages: [], tokens: {} });
9252
9106
  for (const [componentId, needed] of neededExports) {
9253
- const componentFile = join12(uiDir, `${componentId}.tsx`);
9107
+ const componentFile = join11(uiDir, `${componentId}.tsx`);
9254
9108
  const def = getShadcnComponent(componentId);
9255
9109
  if (!existsSync19(componentFile)) {
9256
9110
  if (!def) continue;
@@ -9264,7 +9118,7 @@ async function fixMissingComponentExports(projectRoot) {
9264
9118
  }
9265
9119
  continue;
9266
9120
  }
9267
- const content = readFileSync14(componentFile, "utf-8");
9121
+ const content = readFileSync13(componentFile, "utf-8");
9268
9122
  const exportRe = /export\s+(?:const|function|class)\s+(\w+)|export\s*\{([^}]+)\}/g;
9269
9123
  const existingExports = /* @__PURE__ */ new Set();
9270
9124
  let em;
@@ -9287,7 +9141,7 @@ async function fixMissingComponentExports(projectRoot) {
9287
9141
  }
9288
9142
  }
9289
9143
  async function backfillPageAnalysis(projectRoot) {
9290
- const configPath = join12(projectRoot, "design-system.config.ts");
9144
+ const configPath = join11(projectRoot, "design-system.config.ts");
9291
9145
  if (!existsSync19(configPath)) return;
9292
9146
  try {
9293
9147
  const mgr = new DesignSystemManager8(configPath);
@@ -9299,14 +9153,14 @@ async function backfillPageAnalysis(projectRoot) {
9299
9153
  const isAuth = route.includes("login") || route.includes("register") || route.includes("signup") || route.includes("sign-up");
9300
9154
  let filePath;
9301
9155
  if (route === "/") {
9302
- filePath = join12(projectRoot, "app", "page.tsx");
9156
+ filePath = join11(projectRoot, "app", "page.tsx");
9303
9157
  } else if (isAuth) {
9304
- filePath = join12(projectRoot, "app", "(auth)", route.slice(1), "page.tsx");
9158
+ filePath = join11(projectRoot, "app", "(auth)", route.slice(1), "page.tsx");
9305
9159
  } else {
9306
- filePath = join12(projectRoot, "app", route.slice(1), "page.tsx");
9160
+ filePath = join11(projectRoot, "app", route.slice(1), "page.tsx");
9307
9161
  }
9308
9162
  if (!existsSync19(filePath)) continue;
9309
- const code = readFileSync14(filePath, "utf-8");
9163
+ const code = readFileSync13(filePath, "utf-8");
9310
9164
  if (code.length < 50) continue;
9311
9165
  page.pageAnalysis = analyzePageCode(code);
9312
9166
  changed = true;
@@ -9333,7 +9187,7 @@ async function autoInstallShadcnComponent(componentId, projectRoot) {
9333
9187
  const def = getShadcnComponent(componentId);
9334
9188
  if (!def) return false;
9335
9189
  try {
9336
- const configPath = join12(projectRoot, "design-system.config.ts");
9190
+ const configPath = join11(projectRoot, "design-system.config.ts");
9337
9191
  let config2 = null;
9338
9192
  try {
9339
9193
  const mgr = new DesignSystemManager8(configPath);
@@ -9342,10 +9196,10 @@ async function autoInstallShadcnComponent(componentId, projectRoot) {
9342
9196
  }
9343
9197
  const generator = new ComponentGenerator3(config2 || { components: [], pages: [], tokens: {} });
9344
9198
  const code = await generator.generate(def);
9345
- const uiDir = join12(projectRoot, "components", "ui");
9199
+ const uiDir = join11(projectRoot, "components", "ui");
9346
9200
  const { mkdirSync: mkdirSync9 } = await import("fs");
9347
9201
  mkdirSync9(uiDir, { recursive: true });
9348
- writeFileSync10(join12(uiDir, `${componentId}.tsx`), code, "utf-8");
9202
+ writeFileSync10(join11(uiDir, `${componentId}.tsx`), code, "utf-8");
9349
9203
  return true;
9350
9204
  } catch {
9351
9205
  return false;
@@ -9468,12 +9322,12 @@ async function openBrowser(url) {
9468
9322
  }
9469
9323
  }
9470
9324
  function startDevServer(projectRoot) {
9471
- const packageJsonPath = resolve11(projectRoot, "package.json");
9325
+ const packageJsonPath = resolve10(projectRoot, "package.json");
9472
9326
  if (!existsSync19(packageJsonPath)) {
9473
9327
  throw new Error('package.json not found. Run "coherent init" first.');
9474
9328
  }
9475
- const hasPnpm = existsSync19(resolve11(projectRoot, "pnpm-lock.yaml"));
9476
- 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"));
9477
9331
  const command = hasPnpm ? "pnpm" : hasNpm ? "npm" : "npx";
9478
9332
  const args = hasPnpm ? ["dev", "--turbo"] : hasNpm ? ["run", "dev", "--", "--turbo"] : ["next", "dev", "--turbo"];
9479
9333
  const child = spawn(command, args, {
@@ -9520,7 +9374,7 @@ async function previewCommand() {
9520
9374
  if (needsGlobalsFix(projectRoot)) {
9521
9375
  spinner.text = "Fixing globals.css...";
9522
9376
  try {
9523
- const dsm = new DesignSystemManager8(resolve11(projectRoot, "design-system.config.ts"));
9377
+ const dsm = new DesignSystemManager8(resolve10(projectRoot, "design-system.config.ts"));
9524
9378
  await dsm.load();
9525
9379
  const config2 = dsm.getConfig();
9526
9380
  fixGlobalsCss(projectRoot, config2);
@@ -9559,8 +9413,8 @@ async function previewCommand() {
9559
9413
  import chalk15 from "chalk";
9560
9414
  import ora4 from "ora";
9561
9415
  import { spawn as spawn2 } from "child_process";
9562
- import { existsSync as existsSync20, rmSync as rmSync4, readdirSync as readdirSync4 } from "fs";
9563
- 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";
9564
9418
  import { readdir as readdir3, readFile as readFile5, writeFile as writeFile4, mkdir as mkdir4, copyFile as copyFile2 } from "fs/promises";
9565
9419
  var COPY_EXCLUDE = /* @__PURE__ */ new Set([
9566
9420
  "node_modules",
@@ -9582,8 +9436,8 @@ async function copyDir(src, dest) {
9582
9436
  await mkdir4(dest, { recursive: true });
9583
9437
  const entries = await readdir3(src, { withFileTypes: true });
9584
9438
  for (const e of entries) {
9585
- const srcPath = join13(src, e.name);
9586
- const destPath = join13(dest, e.name);
9439
+ const srcPath = join12(src, e.name);
9440
+ const destPath = join12(dest, e.name);
9587
9441
  if (COPY_EXCLUDE.has(e.name)) continue;
9588
9442
  if (e.isDirectory()) {
9589
9443
  await copyDir(srcPath, destPath);
@@ -9594,16 +9448,16 @@ async function copyDir(src, dest) {
9594
9448
  }
9595
9449
  }
9596
9450
  function checkProjectInitialized2(projectRoot) {
9597
- 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"));
9598
9452
  }
9599
9453
  function getPackageManager2(projectRoot) {
9600
- if (existsSync20(resolve12(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
9601
- 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";
9602
9456
  return "npx";
9603
9457
  }
9604
9458
  async function patchNextConfigForExport(outRoot) {
9605
9459
  for (const name of ["next.config.ts", "next.config.mjs", "next.config.js"]) {
9606
- const p = join13(outRoot, name);
9460
+ const p = join12(outRoot, name);
9607
9461
  if (!existsSync20(p)) continue;
9608
9462
  let content = await readFile5(p, "utf-8");
9609
9463
  if (content.includes("ignoreDuringBuilds")) return;
@@ -9619,9 +9473,9 @@ async function buildProduction(projectRoot) {
9619
9473
  const pm = getPackageManager2(projectRoot);
9620
9474
  const command = pm === "pnpm" ? "pnpm" : pm === "npm" ? "npm" : "npx";
9621
9475
  const args = pm === "npx" ? ["next", "build"] : ["run", "build"];
9622
- return new Promise((resolve17, reject) => {
9476
+ return new Promise((resolve16, reject) => {
9623
9477
  const child = spawn2(command, args, { cwd: projectRoot, stdio: "inherit", shell: true });
9624
- 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}`)));
9625
9479
  child.on("error", (error) => reject(new Error(`Failed to start build: ${error.message}`)));
9626
9480
  });
9627
9481
  }
@@ -9645,7 +9499,7 @@ EXPOSE 3000
9645
9499
  \`\`\`
9646
9500
  `;
9647
9501
  async function ensureReadmeDeploySection(outRoot) {
9648
- const readmePath = join13(outRoot, "README.md");
9502
+ const readmePath = join12(outRoot, "README.md");
9649
9503
  if (!existsSync20(readmePath)) return;
9650
9504
  try {
9651
9505
  let content = await readFile5(readmePath, "utf-8");
@@ -9665,22 +9519,22 @@ async function countPages(outRoot) {
9665
9519
  return;
9666
9520
  }
9667
9521
  for (const e of entries) {
9668
- const full = join13(dir, e.name);
9522
+ const full = join12(dir, e.name);
9669
9523
  if (e.isFile() && e.name === "page.tsx") n++;
9670
9524
  else if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "api") await walk(full);
9671
9525
  }
9672
9526
  }
9673
- const appDir = join13(outRoot, "app");
9527
+ const appDir = join12(outRoot, "app");
9674
9528
  if (existsSync20(appDir)) await walk(appDir);
9675
9529
  return n;
9676
9530
  }
9677
9531
  function countComponents(outRoot) {
9678
9532
  let n = 0;
9679
9533
  for (const sub of ["ui", "shared"]) {
9680
- const dir = join13(outRoot, "components", sub);
9534
+ const dir = join12(outRoot, "components", sub);
9681
9535
  if (!existsSync20(dir)) continue;
9682
9536
  try {
9683
- 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;
9684
9538
  } catch {
9685
9539
  }
9686
9540
  }
@@ -9693,7 +9547,7 @@ async function collectImportedPackages2(dir, extensions) {
9693
9547
  async function walk(d) {
9694
9548
  const entries = await readdir3(d, { withFileTypes: true });
9695
9549
  for (const e of entries) {
9696
- const full = join13(d, e.name);
9550
+ const full = join12(d, e.name);
9697
9551
  if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules") {
9698
9552
  await walk(full);
9699
9553
  continue;
@@ -9716,7 +9570,7 @@ async function collectImportedPackages2(dir, extensions) {
9716
9570
  return packages;
9717
9571
  }
9718
9572
  async function findMissingDepsInExport(outRoot) {
9719
- const pkgPath = join13(outRoot, "package.json");
9573
+ const pkgPath = join12(outRoot, "package.json");
9720
9574
  if (!existsSync20(pkgPath)) return [];
9721
9575
  let pkg;
9722
9576
  try {
@@ -9725,7 +9579,7 @@ async function findMissingDepsInExport(outRoot) {
9725
9579
  return [];
9726
9580
  }
9727
9581
  const inDeps = /* @__PURE__ */ new Set([...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})]);
9728
- const codeDirs = [join13(outRoot, "app"), join13(outRoot, "components")];
9582
+ const codeDirs = [join12(outRoot, "app"), join12(outRoot, "components")];
9729
9583
  const extensions = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx"]);
9730
9584
  const imported = /* @__PURE__ */ new Set();
9731
9585
  for (const dir of codeDirs) {
@@ -9737,25 +9591,33 @@ async function findMissingDepsInExport(outRoot) {
9737
9591
  async function stripCoherentArtifacts(outputDir) {
9738
9592
  const removed = [];
9739
9593
  for (const p of ["app/design-system", "app/api/design-system"]) {
9740
- const full = join13(outputDir, p);
9594
+ const full = join12(outputDir, p);
9741
9595
  if (existsSync20(full)) {
9742
9596
  rmSync4(full, { recursive: true, force: true });
9743
9597
  removed.push(p);
9744
9598
  }
9745
9599
  }
9746
- const appNavPath = join13(outputDir, "app", "AppNav.tsx");
9600
+ const appNavPath = join12(outputDir, "app", "AppNav.tsx");
9747
9601
  if (existsSync20(appNavPath)) {
9748
9602
  rmSync4(appNavPath, { force: true });
9749
9603
  removed.push("app/AppNav.tsx");
9750
9604
  }
9751
- const layoutPath = join13(outputDir, "app", "layout.tsx");
9605
+ const layoutPath = join12(outputDir, "app", "layout.tsx");
9752
9606
  if (existsSync20(layoutPath)) {
9753
9607
  let layout = await readFile5(layoutPath, "utf-8");
9754
9608
  layout = layout.replace(/import\s*\{?\s*AppNav\s*\}?\s*from\s*['"][^'"]+['"]\s*\n?/g, "");
9755
9609
  layout = layout.replace(/\s*<AppNav\s*\/?\s*>\s*/g, "\n");
9756
9610
  await writeFile4(layoutPath, layout, "utf-8");
9757
9611
  }
9758
- 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");
9759
9621
  if (existsSync20(guardPath)) {
9760
9622
  let guard = await readFile5(guardPath, "utf-8");
9761
9623
  guard = guard.replace(/['"],?\s*'\/design-system['"],?\s*/g, "");
@@ -9784,14 +9646,14 @@ async function stripCoherentArtifacts(outputDir) {
9784
9646
  ".env.local",
9785
9647
  "recommendations.md"
9786
9648
  ]) {
9787
- const full = join13(outputDir, name);
9649
+ const full = join12(outputDir, name);
9788
9650
  if (existsSync20(full)) {
9789
9651
  rmSync4(full, { force: true });
9790
9652
  removed.push(name);
9791
9653
  }
9792
9654
  }
9793
9655
  for (const dir of [".claude", ".coherent"]) {
9794
- const full = join13(outputDir, dir);
9656
+ const full = join12(outputDir, dir);
9795
9657
  if (existsSync20(full)) {
9796
9658
  rmSync4(full, { recursive: true, force: true });
9797
9659
  removed.push(dir + "/");
@@ -9800,7 +9662,7 @@ async function stripCoherentArtifacts(outputDir) {
9800
9662
  return removed;
9801
9663
  }
9802
9664
  async function exportCommand(options = {}) {
9803
- const outputDir = resolve12(process.cwd(), options.output ?? "./export");
9665
+ const outputDir = resolve11(process.cwd(), options.output ?? "./export");
9804
9666
  const doBuild = options.build !== false;
9805
9667
  const keepDs = options.keepDs === true;
9806
9668
  const spinner = ora4("Preparing export...").start();
@@ -9991,8 +9853,8 @@ async function regenerateDocsCommand() {
9991
9853
 
9992
9854
  // src/commands/fix.ts
9993
9855
  import chalk18 from "chalk";
9994
- import { readdirSync as readdirSync5, readFileSync as readFileSync15, existsSync as existsSync21, writeFileSync as writeFileSync11, rmSync as rmSync5, mkdirSync as mkdirSync7 } from "fs";
9995
- 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";
9996
9858
  import {
9997
9859
  DesignSystemManager as DesignSystemManager11,
9998
9860
  ComponentManager as ComponentManager5,
@@ -10016,9 +9878,9 @@ function extractComponentIdsFromCode2(code) {
10016
9878
  function listTsxFiles(dir) {
10017
9879
  const files = [];
10018
9880
  try {
10019
- const entries = readdirSync5(dir, { withFileTypes: true });
9881
+ const entries = readdirSync4(dir, { withFileTypes: true });
10020
9882
  for (const e of entries) {
10021
- const full = join14(dir, e.name);
9883
+ const full = join13(dir, e.name);
10022
9884
  if (e.isDirectory() && e.name !== "node_modules" && !e.name.startsWith(".")) {
10023
9885
  files.push(...listTsxFiles(full));
10024
9886
  } else if (e.isFile() && e.name.endsWith(".tsx")) {
@@ -10047,7 +9909,7 @@ async function fixCommand(opts = {}) {
10047
9909
  console.log(chalk18.cyan("\ncoherent fix\n"));
10048
9910
  }
10049
9911
  if (!skipCache) {
10050
- const nextDir = join14(projectRoot, ".next");
9912
+ const nextDir = join13(projectRoot, ".next");
10051
9913
  if (existsSync21(nextDir)) {
10052
9914
  if (!dryRun) rmSync5(nextDir, { recursive: true, force: true });
10053
9915
  fixes.push("Cleared build cache");
@@ -10070,12 +9932,12 @@ async function fixCommand(opts = {}) {
10070
9932
  }
10071
9933
  }
10072
9934
  }
10073
- const appDir = resolve13(projectRoot, "app");
9935
+ const appDir = resolve12(projectRoot, "app");
10074
9936
  const allTsxFiles = listTsxFiles(appDir);
10075
- const componentsTsxFiles = listTsxFiles(resolve13(projectRoot, "components"));
9937
+ const componentsTsxFiles = listTsxFiles(resolve12(projectRoot, "components"));
10076
9938
  const allComponentIds = /* @__PURE__ */ new Set();
10077
9939
  for (const file of [...allTsxFiles, ...componentsTsxFiles]) {
10078
- const content = readFileSync15(file, "utf-8");
9940
+ const content = readFileSync14(file, "utf-8");
10079
9941
  extractComponentIdsFromCode2(content).forEach((id) => allComponentIds.add(id));
10080
9942
  }
10081
9943
  let dsm = null;
@@ -10094,7 +9956,7 @@ async function fixCommand(opts = {}) {
10094
9956
  missingComponents.push(id);
10095
9957
  } else {
10096
9958
  const fileName = toKebabCase(id) + ".tsx";
10097
- const filePath = resolve13(projectRoot, "components", "ui", fileName);
9959
+ const filePath = resolve12(projectRoot, "components", "ui", fileName);
10098
9960
  if (!existsSync21(filePath)) missingFiles.push(id);
10099
9961
  }
10100
9962
  }
@@ -10122,8 +9984,8 @@ async function fixCommand(opts = {}) {
10122
9984
  const generator = new ComponentGenerator4(updatedConfig);
10123
9985
  const code = await generator.generate(component);
10124
9986
  const fileName = toKebabCase(component.name) + ".tsx";
10125
- const filePath = resolve13(projectRoot, "components", "ui", fileName);
10126
- mkdirSync7(resolve13(projectRoot, "components", "ui"), { recursive: true });
9987
+ const filePath = resolve12(projectRoot, "components", "ui", fileName);
9988
+ mkdirSync7(resolve12(projectRoot, "components", "ui"), { recursive: true });
10127
9989
  await writeFile(filePath, code);
10128
9990
  installed++;
10129
9991
  }
@@ -10144,7 +10006,7 @@ async function fixCommand(opts = {}) {
10144
10006
  const userTsxFiles = allTsxFiles.filter((f) => !f.includes("/design-system/"));
10145
10007
  let syntaxFixed = 0;
10146
10008
  for (const file of userTsxFiles) {
10147
- const content = readFileSync15(file, "utf-8");
10009
+ const content = readFileSync14(file, "utf-8");
10148
10010
  const fixed = fixUnescapedLtInJsx(
10149
10011
  fixEscapedClosingQuotes(sanitizeMetadataStrings(ensureUseClientIfNeeded(content)))
10150
10012
  );
@@ -10162,7 +10024,7 @@ async function fixCommand(opts = {}) {
10162
10024
  let qualityFixCount = 0;
10163
10025
  const qualityFixDetails = [];
10164
10026
  for (const file of userTsxFiles) {
10165
- const content = readFileSync15(file, "utf-8");
10027
+ const content = readFileSync14(file, "utf-8");
10166
10028
  const { code: autoFixed, fixes: fileFixes } = await autoFixCode(content);
10167
10029
  if (autoFixed !== content) {
10168
10030
  if (!dryRun) writeFileSync11(file, autoFixed, "utf-8");
@@ -10181,7 +10043,7 @@ async function fixCommand(opts = {}) {
10181
10043
  let totalWarnings = 0;
10182
10044
  const fileIssues = [];
10183
10045
  for (const file of allTsxFiles) {
10184
- const code = dryRun ? readFileSync15(file, "utf-8") : readFileSync15(file, "utf-8");
10046
+ const code = dryRun ? readFileSync14(file, "utf-8") : readFileSync14(file, "utf-8");
10185
10047
  const relativePath = file.replace(projectRoot + "/", "");
10186
10048
  const baseName = file.split("/").pop() || "";
10187
10049
  const isAuthPage = relativePath.includes("(auth)");
@@ -10303,16 +10165,16 @@ async function fixCommand(opts = {}) {
10303
10165
 
10304
10166
  // src/commands/check.ts
10305
10167
  import chalk19 from "chalk";
10306
- import { resolve as resolve14 } from "path";
10307
- 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";
10308
10170
  import { loadManifest as loadManifest11 } from "@getcoherent/core";
10309
10171
  var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["node_modules", "design-system"]);
10310
10172
  function findTsxFiles(dir) {
10311
10173
  const results = [];
10312
10174
  try {
10313
- const entries = readdirSync6(dir);
10175
+ const entries = readdirSync5(dir);
10314
10176
  for (const entry of entries) {
10315
- const full = resolve14(dir, entry);
10177
+ const full = resolve13(dir, entry);
10316
10178
  const stat = statSync2(full);
10317
10179
  if (stat.isDirectory() && !entry.startsWith(".") && !EXCLUDED_DIRS.has(entry)) {
10318
10180
  results.push(...findTsxFiles(full));
@@ -10347,7 +10209,7 @@ async function checkCommand(opts = {}) {
10347
10209
  } catch {
10348
10210
  }
10349
10211
  if (!skipPages) {
10350
- const appDir = resolve14(projectRoot, "app");
10212
+ const appDir = resolve13(projectRoot, "app");
10351
10213
  const files = findTsxFiles(appDir);
10352
10214
  result.pages.total = files.length;
10353
10215
  if (!opts.json) console.log(chalk19.cyan("\n \u{1F4C4} Pages") + chalk19.dim(` (${files.length} scanned)
@@ -10361,7 +10223,7 @@ async function checkCommand(opts = {}) {
10361
10223
  "NATIVE_TABLE"
10362
10224
  ]);
10363
10225
  for (const file of files) {
10364
- const code = readFileSync16(file, "utf-8");
10226
+ const code = readFileSync15(file, "utf-8");
10365
10227
  const relativePath = file.replace(projectRoot + "/", "");
10366
10228
  const baseName = file.split("/").pop() || "";
10367
10229
  const isAuthPage = relativePath.includes("(auth)");
@@ -10403,7 +10265,7 @@ async function checkCommand(opts = {}) {
10403
10265
  routeSet.add("/");
10404
10266
  routeSet.add("#");
10405
10267
  for (const file of files) {
10406
- const code = readFileSync16(file, "utf-8");
10268
+ const code = readFileSync15(file, "utf-8");
10407
10269
  const relativePath = file.replace(projectRoot + "/", "");
10408
10270
  const lines = code.split("\n");
10409
10271
  const linkHrefRe = /href\s*=\s*["'](\/[a-z0-9/-]*)["']/gi;
@@ -10435,7 +10297,7 @@ async function checkCommand(opts = {}) {
10435
10297
  const manifest = await loadManifest11(project.root);
10436
10298
  if (manifest.shared.length > 0) {
10437
10299
  for (const entry of manifest.shared) {
10438
- const fullPath = resolve14(project.root, entry.file);
10300
+ const fullPath = resolve13(project.root, entry.file);
10439
10301
  if (!existsSync22(fullPath)) {
10440
10302
  result.pages.withErrors++;
10441
10303
  if (!opts.json) console.log(chalk19.red(`
@@ -10460,7 +10322,7 @@ async function checkCommand(opts = {}) {
10460
10322
  let _staleUsedIn = 0;
10461
10323
  let _nameMismatch = 0;
10462
10324
  for (const entry of manifest.shared) {
10463
- const filePath = resolve14(projectRoot, entry.file);
10325
+ const filePath = resolve13(projectRoot, entry.file);
10464
10326
  const fileExists = existsSync22(filePath);
10465
10327
  if (!fileExists) {
10466
10328
  _orphaned++;
@@ -10471,7 +10333,7 @@ async function checkCommand(opts = {}) {
10471
10333
  continue;
10472
10334
  }
10473
10335
  try {
10474
- const code = readFileSync16(filePath, "utf-8");
10336
+ const code = readFileSync15(filePath, "utf-8");
10475
10337
  const actualExports = extractExportedComponentNames(code);
10476
10338
  if (actualExports.length > 0 && !actualExports.includes(entry.name)) {
10477
10339
  _nameMismatch++;
@@ -10539,7 +10401,7 @@ async function checkCommand(opts = {}) {
10539
10401
  id: e.id,
10540
10402
  name: e.name,
10541
10403
  type: e.type,
10542
- status: existsSync22(resolve14(projectRoot, e.file)) ? "ok" : "unused",
10404
+ status: existsSync22(resolve13(projectRoot, e.file)) ? "ok" : "unused",
10543
10405
  message: "",
10544
10406
  suggestions: void 0
10545
10407
  }))
@@ -10632,14 +10494,14 @@ import {
10632
10494
  ComponentManager as ComponentManager6,
10633
10495
  loadManifest as loadManifest12,
10634
10496
  generateSharedComponent as generateSharedComponent4,
10635
- integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout4
10497
+ integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout3
10636
10498
  } from "@getcoherent/core";
10637
10499
  import { existsSync as existsSync23 } from "fs";
10638
- import { resolve as resolve15 } from "path";
10500
+ import { resolve as resolve14 } from "path";
10639
10501
 
10640
10502
  // src/utils/ds-files.ts
10641
10503
  import { mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
10642
- import { join as join15, dirname as dirname8 } from "path";
10504
+ import { join as join14, dirname as dirname8 } from "path";
10643
10505
  import { DesignSystemGenerator } from "@getcoherent/core";
10644
10506
  var SHARED_DS_KEYS = [
10645
10507
  "app/design-system/shared/page.tsx",
@@ -10653,7 +10515,7 @@ async function writeDesignSystemFiles(projectRoot, config2, options) {
10653
10515
  const toWrite = options?.sharedOnly ? new Map([...files].filter(([path3]) => SHARED_DS_KEYS.includes(path3))) : files;
10654
10516
  const written = [];
10655
10517
  for (const [relativePath, content] of toWrite) {
10656
- const fullPath = join15(projectRoot, relativePath);
10518
+ const fullPath = join14(projectRoot, relativePath);
10657
10519
  await mkdir5(dirname8(fullPath), { recursive: true });
10658
10520
  await writeFile5(fullPath, content, "utf-8");
10659
10521
  written.push(relativePath);
@@ -10762,10 +10624,10 @@ function createComponentsCommand() {
10762
10624
  \u2705 Created ${result.id} (${result.name}) at ${result.file}
10763
10625
  `));
10764
10626
  if (type === "layout") {
10765
- const updated = await integrateSharedLayoutIntoRootLayout4(project.root);
10627
+ const updated = await integrateSharedLayoutIntoRootLayout3(project.root);
10766
10628
  if (updated) console.log(chalk25.cyan(" Updated app/layout.tsx to use shared layout components.\n"));
10767
10629
  }
10768
- const sharedPagePath = resolve15(project.root, "app/design-system/shared/page.tsx");
10630
+ const sharedPagePath = resolve14(project.root, "app/design-system/shared/page.tsx");
10769
10631
  if (!existsSync23(sharedPagePath)) {
10770
10632
  try {
10771
10633
  const dsm = new DesignSystemManager12(project.configPath);
@@ -10792,7 +10654,7 @@ function createComponentsCommand() {
10792
10654
  import chalk26 from "chalk";
10793
10655
  import ora6 from "ora";
10794
10656
  import { writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
10795
- 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";
10796
10658
  import { existsSync as existsSync24 } from "fs";
10797
10659
  import {
10798
10660
  FigmaClient,
@@ -10805,7 +10667,7 @@ import {
10805
10667
  setSharedMapping,
10806
10668
  generateSharedComponent as generateSharedComponent5,
10807
10669
  generatePagesFromFigma,
10808
- integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout5,
10670
+ integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout4,
10809
10671
  DesignSystemManager as DesignSystemManager13,
10810
10672
  validateConfig,
10811
10673
  FRAMEWORK_VERSIONS as FRAMEWORK_VERSIONS2,
@@ -10940,7 +10802,7 @@ async function importFigmaAction(urlOrKey, opts) {
10940
10802
  stats.filesWritten.push(filePath);
10941
10803
  return;
10942
10804
  }
10943
- const fullPath = join16(projectRoot, filePath);
10805
+ const fullPath = join15(projectRoot, filePath);
10944
10806
  await mkdir6(dirname9(fullPath), { recursive: true });
10945
10807
  await writeFile6(fullPath, content, "utf-8");
10946
10808
  stats.filesWritten.push(filePath);
@@ -11017,7 +10879,7 @@ async function importFigmaAction(urlOrKey, opts) {
11017
10879
  if (dryRun) stats.filesWritten.push(FIGMA_COMPONENT_MAP_FILENAME);
11018
10880
  else
11019
10881
  await writeFile6(
11020
- resolve16(projectRoot, FIGMA_COMPONENT_MAP_FILENAME),
10882
+ resolve15(projectRoot, FIGMA_COMPONENT_MAP_FILENAME),
11021
10883
  JSON.stringify(componentMapObj, null, 2),
11022
10884
  "utf-8"
11023
10885
  );
@@ -11040,7 +10902,7 @@ async function importFigmaAction(urlOrKey, opts) {
11040
10902
  const fullConfig = buildFigmaImportConfig(mergedConfig, pageDefs, intermediate.fileName);
11041
10903
  if (!dryRun) {
11042
10904
  spinner.start("Updating design-system.config.ts...");
11043
- const configPath = resolve16(projectRoot, DESIGN_SYSTEM_CONFIG_PATH);
10905
+ const configPath = resolve15(projectRoot, DESIGN_SYSTEM_CONFIG_PATH);
11044
10906
  const dsm = new DesignSystemManager13(configPath);
11045
10907
  if (existsSync24(configPath)) {
11046
10908
  await dsm.load();
@@ -11071,7 +10933,7 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
11071
10933
  stats.configUpdated = true;
11072
10934
  spinner.succeed("design-system.config.ts updated");
11073
10935
  spinner.start("Ensuring root layout...");
11074
- const layoutPath = join16(projectRoot, "app/layout.tsx");
10936
+ const layoutPath = join15(projectRoot, "app/layout.tsx");
11075
10937
  if (!existsSync24(layoutPath)) {
11076
10938
  await mkdir6(dirname9(layoutPath), { recursive: true });
11077
10939
  await writeFile6(layoutPath, MINIMAL_ROOT_LAYOUT, "utf-8");
@@ -11079,7 +10941,7 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
11079
10941
  }
11080
10942
  spinner.succeed("Root layout OK");
11081
10943
  spinner.start("Integrating shared layout (Header/Footer)...");
11082
- const layoutIntegrated = await integrateSharedLayoutIntoRootLayout5(projectRoot);
10944
+ const layoutIntegrated = await integrateSharedLayoutIntoRootLayout4(projectRoot);
11083
10945
  stats.layoutIntegrated = layoutIntegrated;
11084
10946
  spinner.succeed(layoutIntegrated ? "Layout components wired" : "No layout components to wire");
11085
10947
  spinner.start("Generating Design System viewer...");
@@ -11162,8 +11024,8 @@ async function dsRegenerateCommand() {
11162
11024
  // src/commands/update.ts
11163
11025
  import chalk28 from "chalk";
11164
11026
  import ora8 from "ora";
11165
- import { readFileSync as readFileSync17, existsSync as existsSync25 } from "fs";
11166
- import { join as join17 } from "path";
11027
+ import { readFileSync as readFileSync16, existsSync as existsSync25 } from "fs";
11028
+ import { join as join16 } from "path";
11167
11029
  import { DesignSystemManager as DesignSystemManager15, CLI_VERSION as CLI_VERSION4 } from "@getcoherent/core";
11168
11030
 
11169
11031
  // src/utils/migrations.ts
@@ -11332,20 +11194,20 @@ var EXPECTED_CSS_VARS = [
11332
11194
  "--sidebar-ring"
11333
11195
  ];
11334
11196
  function checkMissingCssVars(projectRoot) {
11335
- const globalsPath = join17(projectRoot, "app", "globals.css");
11197
+ const globalsPath = join16(projectRoot, "app", "globals.css");
11336
11198
  if (!existsSync25(globalsPath)) return [];
11337
11199
  try {
11338
- const content = readFileSync17(globalsPath, "utf-8");
11200
+ const content = readFileSync16(globalsPath, "utf-8");
11339
11201
  return EXPECTED_CSS_VARS.filter((v) => !content.includes(v));
11340
11202
  } catch {
11341
11203
  return [];
11342
11204
  }
11343
11205
  }
11344
11206
  function patchGlobalsCss(projectRoot, missingVars) {
11345
- const globalsPath = join17(projectRoot, "app", "globals.css");
11207
+ const globalsPath = join16(projectRoot, "app", "globals.css");
11346
11208
  if (!existsSync25(globalsPath) || missingVars.length === 0) return;
11347
11209
  const { writeFileSync: writeFileSync13 } = __require("fs");
11348
- let content = readFileSync17(globalsPath, "utf-8");
11210
+ let content = readFileSync16(globalsPath, "utf-8");
11349
11211
  const defaultValues = {
11350
11212
  "--chart-1": "220 70% 50%",
11351
11213
  "--chart-2": "160 60% 45%",
@@ -11423,26 +11285,26 @@ async function undoCommand(options) {
11423
11285
  // src/commands/sync.ts
11424
11286
  import chalk30 from "chalk";
11425
11287
  import ora9 from "ora";
11426
- import { existsSync as existsSync26, readFileSync as readFileSync18 } from "fs";
11427
- 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";
11428
11290
  import { readdir as readdir4, readFile as readFile6 } from "fs/promises";
11429
11291
  import { DesignSystemManager as DesignSystemManager16 } from "@getcoherent/core";
11430
11292
  import { loadManifest as loadManifest13, saveManifest as saveManifest5, findSharedComponent } from "@getcoherent/core";
11431
11293
  function extractTokensFromProject(projectRoot) {
11432
11294
  const lightColors = {};
11433
11295
  const darkColors = {};
11434
- const globalsPath = join18(projectRoot, "app", "globals.css");
11296
+ const globalsPath = join17(projectRoot, "app", "globals.css");
11435
11297
  if (existsSync26(globalsPath)) {
11436
- const css = readFileSync18(globalsPath, "utf-8");
11298
+ const css = readFileSync17(globalsPath, "utf-8");
11437
11299
  const rootMatch = css.match(/:root\s*\{([^}]+)\}/s);
11438
11300
  if (rootMatch) parseVarsInto(rootMatch[1], lightColors);
11439
11301
  const darkMatch = css.match(/\.dark\s*\{([^}]+)\}/s);
11440
11302
  if (darkMatch) parseVarsInto(darkMatch[1], darkColors);
11441
11303
  }
11442
- const layoutPath = join18(projectRoot, "app", "layout.tsx");
11304
+ const layoutPath = join17(projectRoot, "app", "layout.tsx");
11443
11305
  let layoutCode = "";
11444
11306
  if (existsSync26(layoutPath)) {
11445
- layoutCode = readFileSync18(layoutPath, "utf-8");
11307
+ layoutCode = readFileSync17(layoutPath, "utf-8");
11446
11308
  const rootInline = layoutCode.match(/:root\s*\{([^}]+)\}/s);
11447
11309
  if (rootInline && Object.keys(lightColors).length === 0) {
11448
11310
  parseVarsInto(rootInline[1], lightColors);
@@ -11460,7 +11322,7 @@ function extractTokensFromProject(projectRoot) {
11460
11322
  defaultMode = "dark";
11461
11323
  }
11462
11324
  let radius;
11463
- const allCss = [existsSync26(globalsPath) ? readFileSync18(globalsPath, "utf-8") : "", layoutCode].join("\n");
11325
+ const allCss = [existsSync26(globalsPath) ? readFileSync17(globalsPath, "utf-8") : "", layoutCode].join("\n");
11464
11326
  const radiusMatch = allCss.match(/--radius:\s*([^;]+);/);
11465
11327
  if (radiusMatch) radius = radiusMatch[1].trim();
11466
11328
  return {
@@ -11483,7 +11345,7 @@ function parseVarsInto(block, target) {
11483
11345
  }
11484
11346
  async function detectCustomComponents(projectRoot, allPageCode) {
11485
11347
  const results = [];
11486
- const componentsDir = join18(projectRoot, "components");
11348
+ const componentsDir = join17(projectRoot, "components");
11487
11349
  if (!existsSync26(componentsDir)) return results;
11488
11350
  const files = [];
11489
11351
  await walkForTsx(componentsDir, files, ["ui"]);
@@ -11511,7 +11373,7 @@ async function walkForTsx(dir, files, skipDirs) {
11511
11373
  return;
11512
11374
  }
11513
11375
  for (const e of entries) {
11514
- const full = join18(dir, e.name);
11376
+ const full = join17(dir, e.name);
11515
11377
  if (e.isDirectory()) {
11516
11378
  if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
11517
11379
  await walkForTsx(full, files, skipDirs);
@@ -11585,7 +11447,7 @@ async function discoverPages(appDir) {
11585
11447
  return;
11586
11448
  }
11587
11449
  for (const entry of entries) {
11588
- const full = join18(dir, entry.name);
11450
+ const full = join17(dir, entry.name);
11589
11451
  if (entry.isDirectory()) {
11590
11452
  if (["design-system", "api", "_not-found"].includes(entry.name)) continue;
11591
11453
  if (entry.name.startsWith(".")) continue;
@@ -11662,7 +11524,7 @@ async function syncCommand(options = {}) {
11662
11524
  if (dryRun) console.log(chalk30.yellow(" [dry-run] No files will be written\n"));
11663
11525
  const spinner = ora9("Scanning project files...").start();
11664
11526
  try {
11665
- const appDir = join18(project.root, "app");
11527
+ const appDir = join17(project.root, "app");
11666
11528
  if (!existsSync26(appDir)) {
11667
11529
  spinner.fail("No app/ directory found");
11668
11530
  process.exit(1);
@@ -11888,20 +11750,20 @@ async function syncCommand(options = {}) {
11888
11750
  }
11889
11751
 
11890
11752
  // src/utils/update-notifier.ts
11891
- import { existsSync as existsSync27, mkdirSync as mkdirSync8, readFileSync as readFileSync19, writeFileSync as writeFileSync12 } from "fs";
11892
- 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";
11893
11755
  import { homedir } from "os";
11894
11756
  import chalk31 from "chalk";
11895
11757
  import { CLI_VERSION as CLI_VERSION5 } from "@getcoherent/core";
11896
11758
  var DEBUG5 = process.env.COHERENT_DEBUG === "1";
11897
11759
  var PACKAGE_NAME = "@getcoherent/cli";
11898
- var CACHE_DIR = join19(homedir(), ".coherent");
11899
- 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");
11900
11762
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
11901
11763
  function readCache() {
11902
11764
  try {
11903
11765
  if (!existsSync27(CACHE_FILE)) return null;
11904
- const raw = readFileSync19(CACHE_FILE, "utf-8");
11766
+ const raw = readFileSync18(CACHE_FILE, "utf-8");
11905
11767
  return JSON.parse(raw);
11906
11768
  } catch (e) {
11907
11769
  if (DEBUG5) console.error("Failed to read update cache:", e);