@getcoherent/cli 0.5.2 → 0.5.3

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 +351 -162
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -362,6 +362,7 @@ function createMinimalConfig() {
362
362
  }
363
363
  },
364
364
  settings: {
365
+ initialized: false,
365
366
  appType: "multi-page",
366
367
  framework: "next",
367
368
  typescript: true,
@@ -2427,8 +2428,8 @@ async function createAppRouteGroupLayout(projectPath) {
2427
2428
  // src/commands/chat.ts
2428
2429
  import chalk13 from "chalk";
2429
2430
  import ora2 from "ora";
2430
- import { resolve as resolve9 } from "path";
2431
- import { existsSync as existsSync16, readFileSync as readFileSync10, mkdirSync as mkdirSync6 } from "fs";
2431
+ import { resolve as resolve9, relative as relative2 } from "path";
2432
+ import { existsSync as existsSync16, readFileSync as readFileSync10, mkdirSync as mkdirSync6, readdirSync as readdirSync2 } from "fs";
2432
2433
  import {
2433
2434
  DesignSystemManager as DesignSystemManager7,
2434
2435
  ComponentManager as ComponentManager4,
@@ -4605,6 +4606,26 @@ FEW-SHOT EXAMPLE \u2014 correct stat card in pageCode (follow this pattern exact
4605
4606
  \`\`\`
4606
4607
  Key: CardTitle is text-sm font-medium (NOT text-lg). Metric is text-2xl font-bold. Subtext is text-xs text-muted-foreground. Icon is size-4 text-muted-foreground.
4607
4608
 
4609
+ SURGICAL MODIFICATION RULES (CRITICAL for incremental edits):
4610
+ - When modifying an existing page, return the COMPLETE page code
4611
+ - Change ONLY the specific section, component, or element the user requested
4612
+ - Do NOT modify imports unless the change requires new imports
4613
+ - Do NOT change state variables, event handlers, or data in unrelated sections
4614
+ - Do NOT restyle sections the user did not mention
4615
+ - Preserve all existing className values on unchanged elements
4616
+ - If the user asks to change a "section" or "block", identify it by heading, content, or position
4617
+
4618
+ Component Promotion Rules:
4619
+ - When the user asks to "make X a shared component" or "reuse X across pages":
4620
+ - Use request type "promote-and-link"
4621
+ - Extract the JSX block into a separate component file
4622
+ - Replace inline code with the component import on all specified pages
4623
+
4624
+ Global Component Change Rules:
4625
+ - When the user asks to change "all cards" or "every button" or similar:
4626
+ - If the pattern is already a shared component, modify the shared component file
4627
+ - If the pattern is inline across pages, first promote it to a shared component, then modify it
4628
+
4608
4629
  OPTIONAL UX RECOMMENDATIONS:
4609
4630
  If you see opportunities to improve UX (accessibility, layout, consistency, responsiveness, visual hierarchy), add a short markdown block in "uxRecommendations". Otherwise omit it.
4610
4631
 
@@ -4712,8 +4733,8 @@ async function ensureAuthRouteGroup(projectRoot) {
4712
4733
  const guardPath = join6(projectRoot, "app", "ShowWhenNotAuthRoute.tsx");
4713
4734
  const rootLayoutPath = join6(projectRoot, "app", "layout.tsx");
4714
4735
  if (!existsSync9(authLayoutPath)) {
4715
- const { mkdir: mkdir7 } = await import("fs/promises");
4716
- await mkdir7(join6(projectRoot, "app", "(auth)"), { recursive: true });
4736
+ const { mkdir: mkdir8 } = await import("fs/promises");
4737
+ await mkdir8(join6(projectRoot, "app", "(auth)"), { recursive: true });
4717
4738
  await writeFile2(authLayoutPath, AUTH_LAYOUT, "utf-8");
4718
4739
  }
4719
4740
  if (!existsSync9(guardPath)) {
@@ -5885,7 +5906,7 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
5885
5906
 
5886
5907
  // src/commands/chat/modification-handler.ts
5887
5908
  import { resolve as resolve7 } from "path";
5888
- import { mkdir as mkdir3 } from "fs/promises";
5909
+ import { mkdir as mkdir4 } from "fs/promises";
5889
5910
  import { dirname as dirname6 } from "path";
5890
5911
  import chalk11 from "chalk";
5891
5912
  import {
@@ -6673,11 +6694,68 @@ function formatIssues(issues) {
6673
6694
  }
6674
6695
  return lines.join("\n");
6675
6696
  }
6697
+ function checkDesignConsistency(code) {
6698
+ const warnings = [];
6699
+ const hexPattern = /\[#[0-9a-fA-F]{3,8}\]/g;
6700
+ for (const match of code.matchAll(hexPattern)) {
6701
+ warnings.push({
6702
+ type: "hardcoded-color",
6703
+ message: `Hardcoded color ${match[0]} \u2014 use a design token (e.g., bg-primary) instead`
6704
+ });
6705
+ }
6706
+ const spacingPattern = /[pm][trblxy]?-\[\d+px\]/g;
6707
+ for (const match of code.matchAll(spacingPattern)) {
6708
+ warnings.push({
6709
+ type: "arbitrary-spacing",
6710
+ message: `Arbitrary spacing ${match[0]} \u2014 use Tailwind spacing scale instead`
6711
+ });
6712
+ }
6713
+ return warnings;
6714
+ }
6715
+ function verifyIncrementalEdit(before, after) {
6716
+ const issues = [];
6717
+ const hookPattern = /\buse[A-Z]\w+\s*\(/;
6718
+ if (hookPattern.test(after) && !after.includes("'use client'") && !after.includes('"use client"')) {
6719
+ issues.push({
6720
+ type: "missing-use-client",
6721
+ message: 'Code uses React hooks but missing "use client" directive'
6722
+ });
6723
+ }
6724
+ if (!after.includes("export default")) {
6725
+ issues.push({
6726
+ type: "missing-default-export",
6727
+ message: "Missing default export \u2014 page component must have a default export"
6728
+ });
6729
+ }
6730
+ const importRegex = /import\s+\{([^}]+)\}\s+from/g;
6731
+ const beforeImports = /* @__PURE__ */ new Set();
6732
+ const afterImports = /* @__PURE__ */ new Set();
6733
+ for (const match of before.matchAll(importRegex)) {
6734
+ match[1].split(",").forEach((s) => beforeImports.add(s.trim()));
6735
+ }
6736
+ for (const match of after.matchAll(importRegex)) {
6737
+ match[1].split(",").forEach((s) => afterImports.add(s.trim()));
6738
+ }
6739
+ for (const symbol of beforeImports) {
6740
+ if (!afterImports.has(symbol) && symbol.length > 0) {
6741
+ const codeWithoutImports = after.replace(/^import\s+.*$/gm, "");
6742
+ const symbolRegex = new RegExp(`\\b${symbol}\\b`);
6743
+ if (symbolRegex.test(codeWithoutImports)) {
6744
+ issues.push({
6745
+ type: "missing-import",
6746
+ symbol,
6747
+ message: `Import for "${symbol}" was removed but symbol is still used in code`
6748
+ });
6749
+ }
6750
+ }
6751
+ }
6752
+ return issues;
6753
+ }
6676
6754
 
6677
6755
  // src/commands/chat/code-generator.ts
6678
6756
  import { resolve as resolve6 } from "path";
6679
6757
  import { existsSync as existsSync14 } from "fs";
6680
- import { mkdir as mkdir2, readFile as readFile5 } from "fs/promises";
6758
+ import { mkdir as mkdir3 } from "fs/promises";
6681
6759
  import { dirname as dirname5 } from "path";
6682
6760
  import {
6683
6761
  ComponentGenerator as ComponentGenerator2,
@@ -6686,6 +6764,39 @@ import {
6686
6764
  } from "@getcoherent/core";
6687
6765
  import { integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout2, generateSharedComponent as generateSharedComponent2 } from "@getcoherent/core";
6688
6766
  import chalk9 from "chalk";
6767
+
6768
+ // src/utils/file-hashes.ts
6769
+ import { createHash } from "crypto";
6770
+ import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir2 } from "fs/promises";
6771
+ import { join as join9 } from "path";
6772
+ var HASHES_FILE = ".coherent/file-hashes.json";
6773
+ async function computeFileHash(filePath) {
6774
+ const content = await readFile5(filePath, "utf-8");
6775
+ return createHash("sha256").update(content).digest("hex");
6776
+ }
6777
+ async function loadHashes(projectRoot) {
6778
+ try {
6779
+ const raw = await readFile5(join9(projectRoot, HASHES_FILE), "utf-8");
6780
+ return JSON.parse(raw);
6781
+ } catch {
6782
+ return {};
6783
+ }
6784
+ }
6785
+ async function saveHashes(projectRoot, hashes) {
6786
+ const dir = join9(projectRoot, ".coherent");
6787
+ await mkdir2(dir, { recursive: true });
6788
+ await writeFile4(join9(projectRoot, HASHES_FILE), JSON.stringify(hashes, null, 2) + "\n");
6789
+ }
6790
+ async function isManuallyEdited(filePath, storedHash) {
6791
+ try {
6792
+ const currentHash = await computeFileHash(filePath);
6793
+ return currentHash !== storedHash;
6794
+ } catch {
6795
+ return false;
6796
+ }
6797
+ }
6798
+
6799
+ // src/commands/chat/code-generator.ts
6689
6800
  async function validateAndFixGeneratedCode(projectRoot, code, options = {}) {
6690
6801
  const fixes = [];
6691
6802
  let fixed = fixEscapedClosingQuotes(code);
@@ -6748,58 +6859,71 @@ async function regeneratePage(pageId, config2, projectRoot) {
6748
6859
  const route = page.route || "/";
6749
6860
  const isAuth = isAuthRoute(route) || isAuthRoute(page.name || page.id || "");
6750
6861
  const filePath = routeToFsPath(projectRoot, route, isAuth);
6751
- await mkdir2(dirname5(filePath), { recursive: true });
6862
+ await mkdir3(dirname5(filePath), { recursive: true });
6752
6863
  await writeFile(filePath, code);
6753
6864
  }
6754
- async function regenerateLayout(config2, projectRoot) {
6755
- const layout = config2.pages[0]?.layout || "centered";
6865
+ async function canOverwriteShared(projectRoot, componentFile, storedHashes) {
6866
+ const filePath = resolve6(projectRoot, componentFile);
6867
+ if (!existsSync14(filePath)) return true;
6868
+ const storedHash = storedHashes[componentFile];
6869
+ if (!storedHash) return true;
6870
+ const edited = await isManuallyEdited(filePath, storedHash);
6871
+ if (edited) {
6872
+ console.log(chalk9.yellow(` \u26A0 Skipping ${componentFile} \u2014 manually edited since last generation`));
6873
+ }
6874
+ return !edited;
6875
+ }
6876
+ async function regenerateLayout(config2, projectRoot, options = { navChanged: false }) {
6756
6877
  const appType = config2.settings.appType || "multi-page";
6757
6878
  const generator = new PageGenerator(config2);
6758
- const code = await generator.generateLayout(layout, appType, { skipNav: true });
6759
- const layoutPath = resolve6(projectRoot, "app", "layout.tsx");
6760
- await writeFile(layoutPath, code);
6879
+ const initialized = config2.settings.initialized !== false;
6880
+ const hashes = options.storedHashes ?? {};
6881
+ if (!initialized) {
6882
+ const layout = config2.pages[0]?.layout || "centered";
6883
+ const code = await generator.generateLayout(layout, appType, { skipNav: true });
6884
+ await writeFile(resolve6(projectRoot, "app", "layout.tsx"), code);
6885
+ }
6761
6886
  if (config2.navigation?.enabled && appType === "multi-page") {
6762
6887
  const navType = config2.navigation.type || "header";
6763
- if (navType === "header" || navType === "both") {
6764
- const headerCode = generator.generateSharedHeaderCode();
6765
- await generateSharedComponent2(projectRoot, {
6766
- name: "Header",
6767
- type: "layout",
6768
- code: headerCode,
6769
- description: "Main site header with navigation and theme toggle",
6770
- usedIn: ["app/layout.tsx"],
6771
- overwrite: true
6772
- });
6773
- }
6774
- let shouldOverwriteFooter = false;
6775
- try {
6776
- const footerPath = resolve6(projectRoot, "components", "shared", "footer.tsx");
6777
- const existing = await readFile5(footerPath, "utf-8");
6778
- shouldOverwriteFooter = existing.includes("Coherent Design Method");
6779
- } catch {
6780
- shouldOverwriteFooter = true;
6781
- }
6782
- if (shouldOverwriteFooter) {
6783
- const footerCode = generator.generateSharedFooterCode();
6784
- await generateSharedComponent2(projectRoot, {
6785
- name: "Footer",
6786
- type: "layout",
6787
- code: footerCode,
6788
- description: "Site footer",
6789
- usedIn: ["app/layout.tsx"],
6790
- overwrite: true
6791
- });
6792
- }
6793
- if (navType === "sidebar" || navType === "both") {
6794
- const sidebarCode = generator.generateSharedSidebarCode();
6795
- await generateSharedComponent2(projectRoot, {
6796
- name: "Sidebar",
6797
- type: "layout",
6798
- code: sidebarCode,
6799
- description: "Vertical sidebar navigation with collapsible sections",
6800
- usedIn: ["app/(app)/layout.tsx"],
6801
- overwrite: true
6802
- });
6888
+ const shouldRegenShared = !initialized || options.navChanged;
6889
+ if (shouldRegenShared) {
6890
+ if (navType === "header" || navType === "both") {
6891
+ if (await canOverwriteShared(projectRoot, "components/shared/header.tsx", hashes)) {
6892
+ const headerCode = generator.generateSharedHeaderCode();
6893
+ await generateSharedComponent2(projectRoot, {
6894
+ name: "Header",
6895
+ type: "layout",
6896
+ code: headerCode,
6897
+ description: "Main site header with navigation and theme toggle",
6898
+ usedIn: ["app/layout.tsx"],
6899
+ overwrite: true
6900
+ });
6901
+ }
6902
+ }
6903
+ if (await canOverwriteShared(projectRoot, "components/shared/footer.tsx", hashes)) {
6904
+ const footerCode = generator.generateSharedFooterCode();
6905
+ await generateSharedComponent2(projectRoot, {
6906
+ name: "Footer",
6907
+ type: "layout",
6908
+ code: footerCode,
6909
+ description: "Site footer",
6910
+ usedIn: ["app/layout.tsx"],
6911
+ overwrite: true
6912
+ });
6913
+ }
6914
+ if (navType === "sidebar" || navType === "both") {
6915
+ if (await canOverwriteShared(projectRoot, "components/shared/sidebar.tsx", hashes)) {
6916
+ const sidebarCode = generator.generateSharedSidebarCode();
6917
+ await generateSharedComponent2(projectRoot, {
6918
+ name: "Sidebar",
6919
+ type: "layout",
6920
+ code: sidebarCode,
6921
+ description: "Vertical sidebar navigation with collapsible sections",
6922
+ usedIn: ["app/(app)/layout.tsx"],
6923
+ overwrite: true
6924
+ });
6925
+ }
6926
+ }
6803
6927
  }
6804
6928
  }
6805
6929
  try {
@@ -6854,7 +6978,7 @@ export default function AppLayout({
6854
6978
  }
6855
6979
  `;
6856
6980
  }
6857
- async function regenerateFiles(modified, config2, projectRoot) {
6981
+ async function regenerateFiles(modified, config2, projectRoot, options = { navChanged: false }) {
6858
6982
  const componentIds = /* @__PURE__ */ new Set();
6859
6983
  const pageIds = /* @__PURE__ */ new Set();
6860
6984
  for (const item of modified) {
@@ -6865,7 +6989,10 @@ async function regenerateFiles(modified, config2, projectRoot) {
6865
6989
  }
6866
6990
  }
6867
6991
  if (config2.navigation?.enabled && modified.length > 0) {
6868
- await regenerateLayout(config2, projectRoot);
6992
+ await regenerateLayout(config2, projectRoot, {
6993
+ navChanged: options.navChanged,
6994
+ storedHashes: options.storedHashes
6995
+ });
6869
6996
  }
6870
6997
  if (componentIds.size > 0) {
6871
6998
  const twGen = new TailwindConfigGenerator(config2);
@@ -7592,7 +7719,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7592
7719
  await ensureAuthRouteGroup(projectRoot);
7593
7720
  }
7594
7721
  const filePath = routeToFsPath(projectRoot, route, isAuth);
7595
- await mkdir3(dirname6(filePath), { recursive: true });
7722
+ await mkdir4(dirname6(filePath), { recursive: true });
7596
7723
  const { fixedCode, fixes: postFixes } = await validateAndFixGeneratedCode(projectRoot, finalPageCode, {
7597
7724
  isPage: true
7598
7725
  });
@@ -7682,6 +7809,12 @@ Rules:
7682
7809
  \u{1F50D} Quality check for ${page.name || page.id}:`));
7683
7810
  console.log(chalk11.dim(report));
7684
7811
  }
7812
+ const consistency = checkDesignConsistency(codeToWrite);
7813
+ if (consistency.length > 0) {
7814
+ console.log(chalk11.yellow(`
7815
+ \u{1F3A8} Design consistency for ${page.name || page.id}:`));
7816
+ consistency.forEach((w) => console.log(chalk11.dim(` \u26A0 [${w.type}] ${w.message}`)));
7817
+ }
7685
7818
  }
7686
7819
  }
7687
7820
  return {
@@ -7747,6 +7880,12 @@ ${routeRules}
7747
7880
  ${pagesCtx}`
7748
7881
  );
7749
7882
  if (DEBUG2) console.log(chalk11.dim(` [update-page] AI returned ${resolvedPageCode.length} chars`));
7883
+ const editIssues = verifyIncrementalEdit(currentCode, resolvedPageCode);
7884
+ if (editIssues.length > 0) {
7885
+ console.log(chalk11.yellow(`
7886
+ \u26A0 Incremental edit issues for ${pageDef.name || pageDef.id}:`));
7887
+ editIssues.forEach((issue) => console.log(chalk11.dim(` [${issue.type}] ${issue.message}`)));
7888
+ }
7750
7889
  } else {
7751
7890
  console.log(chalk11.yellow(" \u26A0 AI provider does not support editPageCode"));
7752
7891
  }
@@ -7771,7 +7910,7 @@ ${pagesCtx}`
7771
7910
  if (installed.length > 0) {
7772
7911
  result.modified = [...result.modified, ...installed.map((id) => `component:${id}`)];
7773
7912
  }
7774
- await mkdir3(dirname6(absPath), { recursive: true });
7913
+ await mkdir4(dirname6(absPath), { recursive: true });
7775
7914
  const { fixedCode, fixes: postFixes } = await validateAndFixGeneratedCode(projectRoot, resolvedPageCode, {
7776
7915
  isPage: true
7777
7916
  });
@@ -7833,6 +7972,12 @@ ${pagesCtx}`
7833
7972
  \u{1F50D} Quality check for ${pageDef.name || pageDef.id}:`));
7834
7973
  console.log(chalk11.dim(report));
7835
7974
  }
7975
+ const consistency = checkDesignConsistency(codeToWrite);
7976
+ if (consistency.length > 0) {
7977
+ console.log(chalk11.yellow(`
7978
+ \u{1F3A8} Design consistency for ${pageDef.name || pageDef.id}:`));
7979
+ consistency.forEach((w) => console.log(chalk11.dim(` \u26A0 [${w.type}] ${w.message}`)));
7980
+ }
7836
7981
  } else {
7837
7982
  try {
7838
7983
  let code = await readFile(absPath);
@@ -7889,6 +8034,15 @@ ${pagesCtx}`
7889
8034
  }
7890
8035
  }
7891
8036
 
8037
+ // src/utils/nav-snapshot.ts
8038
+ function takeNavSnapshot(items) {
8039
+ if (!items || items.length === 0) return "[]";
8040
+ return JSON.stringify(items.map((i) => `${i.label}:${i.href}`).sort());
8041
+ }
8042
+ function hasNavChanged(before, after) {
8043
+ return before !== after;
8044
+ }
8045
+
7892
8046
  // src/commands/chat/interactive.ts
7893
8047
  import chalk12 from "chalk";
7894
8048
  import { resolve as resolve8 } from "path";
@@ -8121,6 +8275,7 @@ async function chatCommand(message, options) {
8121
8275
  }
8122
8276
  spinner.text = "Loading design system configuration...";
8123
8277
  }
8278
+ const storedHashes = await loadHashes(projectRoot);
8124
8279
  const dsm = new DesignSystemManager7(configPath);
8125
8280
  await dsm.load();
8126
8281
  const cm = new ComponentManager4(config2);
@@ -8471,6 +8626,9 @@ async function chatCommand(message, options) {
8471
8626
  if (DEBUG4) console.log(chalk13.dim("[backup] Created snapshot"));
8472
8627
  } catch {
8473
8628
  }
8629
+ const navBefore = takeNavSnapshot(
8630
+ config2.navigation?.items?.map((i) => ({ label: i.label, href: i.route || `/${i.label.toLowerCase()}` }))
8631
+ );
8474
8632
  spinner.start("Applying modifications...");
8475
8633
  const results = [];
8476
8634
  for (const request of normalizedRequests) {
@@ -8618,6 +8776,10 @@ async function chatCommand(message, options) {
8618
8776
  } catch {
8619
8777
  }
8620
8778
  }
8779
+ if (updatedConfig.settings.initialized === false) {
8780
+ updatedConfig.settings.initialized = true;
8781
+ dsm.updateConfig(updatedConfig);
8782
+ }
8621
8783
  spinner.text = "Saving configuration...";
8622
8784
  await dsm.save();
8623
8785
  spinner.succeed("Configuration saved");
@@ -8627,15 +8789,42 @@ async function chatCommand(message, options) {
8627
8789
  scaffoldedPages.forEach(({ route }) => {
8628
8790
  allModified.add(`page:${route.slice(1) || "home"}`);
8629
8791
  });
8792
+ const navAfter = takeNavSnapshot(
8793
+ updatedConfig.navigation?.items?.map((i) => ({
8794
+ label: i.label,
8795
+ href: i.route || `/${i.label.toLowerCase()}`
8796
+ }))
8797
+ );
8798
+ const navChanged = hasNavChanged(navBefore, navAfter);
8630
8799
  if (allModified.size > 0) {
8631
8800
  spinner.start("Regenerating affected files...");
8632
- await regenerateFiles(Array.from(allModified), updatedConfig, projectRoot);
8801
+ await regenerateFiles(Array.from(allModified), updatedConfig, projectRoot, { navChanged, storedHashes });
8633
8802
  spinner.succeed("Files regenerated");
8634
8803
  }
8635
8804
  try {
8636
8805
  fixGlobalsCss(projectRoot, updatedConfig);
8637
8806
  } catch {
8638
8807
  }
8808
+ try {
8809
+ const updatedHashes = { ...storedHashes };
8810
+ const sharedDir = resolve9(projectRoot, "components", "shared");
8811
+ const layoutFile = resolve9(projectRoot, "app", "layout.tsx");
8812
+ const filesToHash = [layoutFile];
8813
+ if (existsSync16(sharedDir)) {
8814
+ for (const f of readdirSync2(sharedDir)) {
8815
+ if (f.endsWith(".tsx")) filesToHash.push(resolve9(sharedDir, f));
8816
+ }
8817
+ }
8818
+ for (const filePath of filesToHash) {
8819
+ if (existsSync16(filePath)) {
8820
+ const rel = relative2(projectRoot, filePath);
8821
+ updatedHashes[rel] = await computeFileHash(filePath);
8822
+ }
8823
+ }
8824
+ await saveHashes(projectRoot, updatedHashes);
8825
+ } catch {
8826
+ if (DEBUG4) console.log(chalk13.dim("[hashes] Could not save file hashes"));
8827
+ }
8639
8828
  const successfulPairs = normalizedRequests.map((request, index) => ({ request, result: results[index] })).filter(({ result }) => result.success);
8640
8829
  if (successfulPairs.length > 0) {
8641
8830
  const changes = successfulPairs.map(({ request }) => ({
@@ -8743,18 +8932,18 @@ import chalk14 from "chalk";
8743
8932
  import ora3 from "ora";
8744
8933
  import { spawn } from "child_process";
8745
8934
  import { existsSync as existsSync19, rmSync as rmSync3, readFileSync as readFileSync13, writeFileSync as writeFileSync10 } from "fs";
8746
- import { resolve as resolve10, join as join11 } from "path";
8935
+ import { resolve as resolve10, join as join12 } from "path";
8747
8936
  import { readdir as readdir2 } from "fs/promises";
8748
8937
  import { DesignSystemManager as DesignSystemManager8, ComponentGenerator as ComponentGenerator3 } from "@getcoherent/core";
8749
8938
 
8750
8939
  // src/utils/file-watcher.ts
8751
8940
  import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, existsSync as existsSync18 } from "fs";
8752
- import { relative as relative3, join as join10 } from "path";
8941
+ import { relative as relative4, join as join11 } from "path";
8753
8942
  import { loadManifest as loadManifest8, saveManifest as saveManifest3 } from "@getcoherent/core";
8754
8943
 
8755
8944
  // src/utils/component-integrity.ts
8756
- import { existsSync as existsSync17, readFileSync as readFileSync11, readdirSync as readdirSync2 } from "fs";
8757
- import { join as join9, relative as relative2 } from "path";
8945
+ import { existsSync as existsSync17, readFileSync as readFileSync11, readdirSync as readdirSync3 } from "fs";
8946
+ import { join as join10, relative as relative3 } from "path";
8758
8947
  function extractExportedComponentNames(code) {
8759
8948
  const names = [];
8760
8949
  let m;
@@ -8780,7 +8969,7 @@ function arraysEqual(a, b) {
8780
8969
  }
8781
8970
  function findPagesImporting(projectRoot, componentName, componentFile) {
8782
8971
  const results = [];
8783
- const appDir = join9(projectRoot, "app");
8972
+ const appDir = join10(projectRoot, "app");
8784
8973
  if (!existsSync17(appDir)) return results;
8785
8974
  const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
8786
8975
  const componentImportPath = componentFile.replace(/\.tsx$/, "").replace(/\.jsx$/, "");
@@ -8792,7 +8981,7 @@ function findPagesImporting(projectRoot, componentName, componentFile) {
8792
8981
  const hasDefaultImport = new RegExp(`import\\s+${componentName}\\s+from\\s+['"]`).test(code);
8793
8982
  const hasPathImport = code.includes(`@/${componentImportPath}`);
8794
8983
  if (hasNamedImport || hasDefaultImport || hasPathImport) {
8795
- results.push(relative2(projectRoot, absPath));
8984
+ results.push(relative3(projectRoot, absPath));
8796
8985
  }
8797
8986
  } catch {
8798
8987
  }
@@ -8800,7 +8989,7 @@ function findPagesImporting(projectRoot, componentName, componentFile) {
8800
8989
  return results;
8801
8990
  }
8802
8991
  function isUsedInLayout(projectRoot, componentName) {
8803
- const layoutPath = join9(projectRoot, "app", "layout.tsx");
8992
+ const layoutPath = join10(projectRoot, "app", "layout.tsx");
8804
8993
  if (!existsSync17(layoutPath)) return false;
8805
8994
  try {
8806
8995
  const code = readFileSync11(layoutPath, "utf-8");
@@ -8811,7 +9000,7 @@ function isUsedInLayout(projectRoot, componentName) {
8811
9000
  }
8812
9001
  function findUnregisteredComponents(projectRoot, manifest) {
8813
9002
  const results = [];
8814
- const componentsDir = join9(projectRoot, "components");
9003
+ const componentsDir = join10(projectRoot, "components");
8815
9004
  if (!existsSync17(componentsDir)) return results;
8816
9005
  const registeredFiles = new Set(manifest.shared.map((s) => s.file));
8817
9006
  const registeredNames = new Set(manifest.shared.map((s) => s.name));
@@ -8821,7 +9010,7 @@ function findUnregisteredComponents(projectRoot, manifest) {
8821
9010
  ["ui", "node_modules"]
8822
9011
  );
8823
9012
  for (const absPath of files) {
8824
- const relFile = relative2(projectRoot, absPath);
9013
+ const relFile = relative3(projectRoot, absPath);
8825
9014
  if (registeredFiles.has(relFile)) continue;
8826
9015
  try {
8827
9016
  const code = readFileSync11(absPath, "utf-8");
@@ -8839,7 +9028,7 @@ function findUnregisteredComponents(projectRoot, manifest) {
8839
9028
  }
8840
9029
  function findInlineDuplicates(projectRoot, manifest) {
8841
9030
  const results = [];
8842
- const appDir = join9(projectRoot, "app");
9031
+ const appDir = join10(projectRoot, "app");
8843
9032
  if (!existsSync17(appDir)) return results;
8844
9033
  const pageFiles = collectFiles(appDir, (name) => name === "page.tsx" || name === "page.jsx");
8845
9034
  for (const absPath of pageFiles) {
@@ -8850,7 +9039,7 @@ function findInlineDuplicates(projectRoot, manifest) {
8850
9039
  } catch {
8851
9040
  continue;
8852
9041
  }
8853
- const relPath = relative2(projectRoot, absPath);
9042
+ const relPath = relative3(projectRoot, absPath);
8854
9043
  for (const shared of manifest.shared) {
8855
9044
  const importPath = shared.file.replace(/\.tsx$/, "").replace(/\.jsx$/, "");
8856
9045
  const isImported = code.includes(`@/${importPath}`) || code.includes(`from './${importPath}'`) || code.includes(`from "../${importPath}"`);
@@ -8869,7 +9058,7 @@ function findInlineDuplicates(projectRoot, manifest) {
8869
9058
  return results;
8870
9059
  }
8871
9060
  function findComponentFileByExportName(projectRoot, componentName) {
8872
- const componentsDir = join9(projectRoot, "components");
9061
+ const componentsDir = join10(projectRoot, "components");
8873
9062
  if (!existsSync17(componentsDir)) return null;
8874
9063
  const files = collectFiles(
8875
9064
  componentsDir,
@@ -8881,7 +9070,7 @@ function findComponentFileByExportName(projectRoot, componentName) {
8881
9070
  const code = readFileSync11(absPath, "utf-8");
8882
9071
  const exports = extractExportedComponentNames(code);
8883
9072
  if (exports.includes(componentName)) {
8884
- return relative2(projectRoot, absPath);
9073
+ return relative3(projectRoot, absPath);
8885
9074
  }
8886
9075
  } catch {
8887
9076
  }
@@ -8891,7 +9080,7 @@ function findComponentFileByExportName(projectRoot, componentName) {
8891
9080
  function removeOrphanedEntries(projectRoot, manifest) {
8892
9081
  const removed = [];
8893
9082
  const valid = manifest.shared.filter((entry) => {
8894
- const filePath = join9(projectRoot, entry.file);
9083
+ const filePath = join10(projectRoot, entry.file);
8895
9084
  if (existsSync17(filePath)) return true;
8896
9085
  removed.push({ id: entry.id, name: entry.name });
8897
9086
  return false;
@@ -8910,7 +9099,7 @@ function reconcileComponents(projectRoot, manifest) {
8910
9099
  };
8911
9100
  const m = { ...manifest, shared: [...manifest.shared], nextId: manifest.nextId };
8912
9101
  m.shared = m.shared.filter((entry) => {
8913
- const filePath = join9(projectRoot, entry.file);
9102
+ const filePath = join10(projectRoot, entry.file);
8914
9103
  if (!existsSync17(filePath)) {
8915
9104
  const newPath = findComponentFileByExportName(projectRoot, entry.name);
8916
9105
  if (newPath) {
@@ -8923,7 +9112,7 @@ function reconcileComponents(projectRoot, manifest) {
8923
9112
  }
8924
9113
  let code;
8925
9114
  try {
8926
- code = readFileSync11(join9(projectRoot, entry.file), "utf-8");
9115
+ code = readFileSync11(join10(projectRoot, entry.file), "utf-8");
8927
9116
  } catch {
8928
9117
  return true;
8929
9118
  }
@@ -9000,12 +9189,12 @@ function collectFiles(dir, filter, skipDirs = []) {
9000
9189
  function walk(d) {
9001
9190
  let entries;
9002
9191
  try {
9003
- entries = readdirSync2(d, { withFileTypes: true });
9192
+ entries = readdirSync3(d, { withFileTypes: true });
9004
9193
  } catch {
9005
9194
  return;
9006
9195
  }
9007
9196
  for (const e of entries) {
9008
- const full = join9(d, e.name);
9197
+ const full = join10(d, e.name);
9009
9198
  if (e.isDirectory()) {
9010
9199
  if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
9011
9200
  walk(full);
@@ -9044,7 +9233,7 @@ function findInlineDuplicatesOfShared(content, manifest) {
9044
9233
  }
9045
9234
  function getWatcherConfig(projectRoot) {
9046
9235
  try {
9047
- const pkgPath = join10(projectRoot, "package.json");
9236
+ const pkgPath = join11(projectRoot, "package.json");
9048
9237
  if (!existsSync18(pkgPath)) return defaultWatcherConfig();
9049
9238
  const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
9050
9239
  const c = pkg?.coherent?.watcher ?? {};
@@ -9069,7 +9258,7 @@ function defaultWatcherConfig() {
9069
9258
  };
9070
9259
  }
9071
9260
  async function handleFileChange(projectRoot, filePath) {
9072
- const relativePath = relative3(projectRoot, filePath).replace(/\\/g, "/");
9261
+ const relativePath = relative4(projectRoot, filePath).replace(/\\/g, "/");
9073
9262
  if (!relativePath.endsWith(".tsx") && !relativePath.endsWith(".ts")) return;
9074
9263
  if (relativePath.includes("node_modules") || relativePath.includes(".next")) return;
9075
9264
  let content;
@@ -9119,7 +9308,7 @@ async function handleFileChange(projectRoot, filePath) {
9119
9308
  }
9120
9309
  }
9121
9310
  async function handleFileDelete(projectRoot, filePath) {
9122
- const relativePath = relative3(projectRoot, filePath).replace(/\\/g, "/");
9311
+ const relativePath = relative4(projectRoot, filePath).replace(/\\/g, "/");
9123
9312
  if (!relativePath.startsWith("components/") || relativePath.startsWith("components/ui/")) return;
9124
9313
  try {
9125
9314
  const chalk32 = (await import("chalk")).default;
@@ -9139,7 +9328,7 @@ async function handleFileDelete(projectRoot, filePath) {
9139
9328
  }
9140
9329
  }
9141
9330
  async function detectNewComponent(projectRoot, filePath) {
9142
- const relativePath = relative3(projectRoot, filePath).replace(/\\/g, "/");
9331
+ const relativePath = relative4(projectRoot, filePath).replace(/\\/g, "/");
9143
9332
  if (!relativePath.startsWith("components/") || relativePath.startsWith("components/ui/")) return;
9144
9333
  if (!relativePath.endsWith(".tsx") && !relativePath.endsWith(".jsx")) return;
9145
9334
  try {
@@ -9173,8 +9362,8 @@ function startFileWatcher(projectRoot) {
9173
9362
  let watcher = null;
9174
9363
  let manifestWatcher = null;
9175
9364
  import("chokidar").then((chokidar) => {
9176
- const appGlob = join10(projectRoot, "app", "**", "*.tsx");
9177
- const compGlob = join10(projectRoot, "components", "**", "*.tsx");
9365
+ const appGlob = join11(projectRoot, "app", "**", "*.tsx");
9366
+ const compGlob = join11(projectRoot, "components", "**", "*.tsx");
9178
9367
  watcher = chokidar.default.watch([appGlob, compGlob], {
9179
9368
  ignoreInitial: true,
9180
9369
  awaitWriteFinish: { stabilityThreshold: 500 }
@@ -9186,7 +9375,7 @@ function startFileWatcher(projectRoot) {
9186
9375
  });
9187
9376
  watcher.on("unlink", (fp) => handleFileDelete(projectRoot, fp));
9188
9377
  });
9189
- const manifestPath = join10(projectRoot, "coherent.components.json");
9378
+ const manifestPath = join11(projectRoot, "coherent.components.json");
9190
9379
  if (existsSync18(manifestPath)) {
9191
9380
  import("chokidar").then((chokidar) => {
9192
9381
  manifestWatcher = chokidar.default.watch(manifestPath, { ignoreInitial: true });
@@ -9238,7 +9427,7 @@ function checkDependenciesInstalled(projectRoot) {
9238
9427
  return existsSync19(nodeModulesPath);
9239
9428
  }
9240
9429
  function clearStaleCache(projectRoot) {
9241
- const nextDir = join11(projectRoot, ".next");
9430
+ const nextDir = join12(projectRoot, ".next");
9242
9431
  if (existsSync19(nextDir)) {
9243
9432
  rmSync3(nextDir, { recursive: true, force: true });
9244
9433
  console.log(chalk14.dim(" \u2714 Cleared stale build cache"));
@@ -9259,7 +9448,7 @@ async function listPageFiles(appDir) {
9259
9448
  async function walk(dir) {
9260
9449
  const entries = await readdir2(dir, { withFileTypes: true });
9261
9450
  for (const e of entries) {
9262
- const full = join11(dir, e.name);
9451
+ const full = join12(dir, e.name);
9263
9452
  if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "api" && e.name !== "design-system") await walk(full);
9264
9453
  else if (e.isFile() && e.name === "page.tsx") out.push(full);
9265
9454
  }
@@ -9268,7 +9457,7 @@ async function listPageFiles(appDir) {
9268
9457
  return out;
9269
9458
  }
9270
9459
  async function validateSyntax(projectRoot) {
9271
- const appDir = join11(projectRoot, "app");
9460
+ const appDir = join12(projectRoot, "app");
9272
9461
  const pages = await listPageFiles(appDir);
9273
9462
  for (const file of pages) {
9274
9463
  const content = readFileSync13(file, "utf-8");
@@ -9280,8 +9469,8 @@ async function validateSyntax(projectRoot) {
9280
9469
  }
9281
9470
  }
9282
9471
  async function fixMissingComponentExports(projectRoot) {
9283
- const appDir = join11(projectRoot, "app");
9284
- const uiDir = join11(projectRoot, "components", "ui");
9472
+ const appDir = join12(projectRoot, "app");
9473
+ const uiDir = join12(projectRoot, "components", "ui");
9285
9474
  if (!existsSync19(appDir) || !existsSync19(uiDir)) return;
9286
9475
  const pages = await listPageFiles(appDir);
9287
9476
  const neededExports = /* @__PURE__ */ new Map();
@@ -9296,7 +9485,7 @@ async function fixMissingComponentExports(projectRoot) {
9296
9485
  for (const name of names) neededExports.get(componentId).add(name);
9297
9486
  }
9298
9487
  }
9299
- const configPath = join11(projectRoot, "design-system.config.ts");
9488
+ const configPath = join12(projectRoot, "design-system.config.ts");
9300
9489
  let config2 = null;
9301
9490
  try {
9302
9491
  const mgr = new DesignSystemManager8(configPath);
@@ -9305,7 +9494,7 @@ async function fixMissingComponentExports(projectRoot) {
9305
9494
  }
9306
9495
  const generator = new ComponentGenerator3(config2 || { components: [], pages: [], tokens: {} });
9307
9496
  for (const [componentId, needed] of neededExports) {
9308
- const componentFile = join11(uiDir, `${componentId}.tsx`);
9497
+ const componentFile = join12(uiDir, `${componentId}.tsx`);
9309
9498
  const def = getShadcnComponent(componentId);
9310
9499
  if (!existsSync19(componentFile)) {
9311
9500
  if (!def) continue;
@@ -9342,7 +9531,7 @@ async function fixMissingComponentExports(projectRoot) {
9342
9531
  }
9343
9532
  }
9344
9533
  async function backfillPageAnalysis(projectRoot) {
9345
- const configPath = join11(projectRoot, "design-system.config.ts");
9534
+ const configPath = join12(projectRoot, "design-system.config.ts");
9346
9535
  if (!existsSync19(configPath)) return;
9347
9536
  try {
9348
9537
  const mgr = new DesignSystemManager8(configPath);
@@ -9354,11 +9543,11 @@ async function backfillPageAnalysis(projectRoot) {
9354
9543
  const isAuth = route.includes("login") || route.includes("register") || route.includes("signup") || route.includes("sign-up");
9355
9544
  let filePath;
9356
9545
  if (route === "/") {
9357
- filePath = join11(projectRoot, "app", "page.tsx");
9546
+ filePath = join12(projectRoot, "app", "page.tsx");
9358
9547
  } else if (isAuth) {
9359
- filePath = join11(projectRoot, "app", "(auth)", route.slice(1), "page.tsx");
9548
+ filePath = join12(projectRoot, "app", "(auth)", route.slice(1), "page.tsx");
9360
9549
  } else {
9361
- filePath = join11(projectRoot, "app", route.slice(1), "page.tsx");
9550
+ filePath = join12(projectRoot, "app", route.slice(1), "page.tsx");
9362
9551
  }
9363
9552
  if (!existsSync19(filePath)) continue;
9364
9553
  const code = readFileSync13(filePath, "utf-8");
@@ -9388,7 +9577,7 @@ async function autoInstallShadcnComponent(componentId, projectRoot) {
9388
9577
  const def = getShadcnComponent(componentId);
9389
9578
  if (!def) return false;
9390
9579
  try {
9391
- const configPath = join11(projectRoot, "design-system.config.ts");
9580
+ const configPath = join12(projectRoot, "design-system.config.ts");
9392
9581
  let config2 = null;
9393
9582
  try {
9394
9583
  const mgr = new DesignSystemManager8(configPath);
@@ -9397,10 +9586,10 @@ async function autoInstallShadcnComponent(componentId, projectRoot) {
9397
9586
  }
9398
9587
  const generator = new ComponentGenerator3(config2 || { components: [], pages: [], tokens: {} });
9399
9588
  const code = await generator.generate(def);
9400
- const uiDir = join11(projectRoot, "components", "ui");
9589
+ const uiDir = join12(projectRoot, "components", "ui");
9401
9590
  const { mkdirSync: mkdirSync9 } = await import("fs");
9402
9591
  mkdirSync9(uiDir, { recursive: true });
9403
- writeFileSync10(join11(uiDir, `${componentId}.tsx`), code, "utf-8");
9592
+ writeFileSync10(join12(uiDir, `${componentId}.tsx`), code, "utf-8");
9404
9593
  return true;
9405
9594
  } catch {
9406
9595
  return false;
@@ -9614,9 +9803,9 @@ async function previewCommand() {
9614
9803
  import chalk15 from "chalk";
9615
9804
  import ora4 from "ora";
9616
9805
  import { spawn as spawn2 } from "child_process";
9617
- import { existsSync as existsSync20, rmSync as rmSync4, readdirSync as readdirSync3 } from "fs";
9618
- import { resolve as resolve11, join as join12, dirname as dirname7 } from "path";
9619
- import { readdir as readdir3, readFile as readFile6, writeFile as writeFile4, mkdir as mkdir4, copyFile as copyFile2 } from "fs/promises";
9806
+ import { existsSync as existsSync20, rmSync as rmSync4, readdirSync as readdirSync4 } from "fs";
9807
+ import { resolve as resolve11, join as join13, dirname as dirname7 } from "path";
9808
+ import { readdir as readdir3, readFile as readFile6, writeFile as writeFile5, mkdir as mkdir5, copyFile as copyFile2 } from "fs/promises";
9620
9809
  var COPY_EXCLUDE = /* @__PURE__ */ new Set([
9621
9810
  "node_modules",
9622
9811
  ".next",
@@ -9634,16 +9823,16 @@ var COPY_EXCLUDE = /* @__PURE__ */ new Set([
9634
9823
  ".env.local"
9635
9824
  ]);
9636
9825
  async function copyDir(src, dest) {
9637
- await mkdir4(dest, { recursive: true });
9826
+ await mkdir5(dest, { recursive: true });
9638
9827
  const entries = await readdir3(src, { withFileTypes: true });
9639
9828
  for (const e of entries) {
9640
- const srcPath = join12(src, e.name);
9641
- const destPath = join12(dest, e.name);
9829
+ const srcPath = join13(src, e.name);
9830
+ const destPath = join13(dest, e.name);
9642
9831
  if (COPY_EXCLUDE.has(e.name)) continue;
9643
9832
  if (e.isDirectory()) {
9644
9833
  await copyDir(srcPath, destPath);
9645
9834
  } else {
9646
- await mkdir4(dirname7(destPath), { recursive: true });
9835
+ await mkdir5(dirname7(destPath), { recursive: true });
9647
9836
  await copyFile2(srcPath, destPath);
9648
9837
  }
9649
9838
  }
@@ -9658,7 +9847,7 @@ function getPackageManager2(projectRoot) {
9658
9847
  }
9659
9848
  async function patchNextConfigForExport(outRoot) {
9660
9849
  for (const name of ["next.config.ts", "next.config.mjs", "next.config.js"]) {
9661
- const p = join12(outRoot, name);
9850
+ const p = join13(outRoot, name);
9662
9851
  if (!existsSync20(p)) continue;
9663
9852
  let content = await readFile6(p, "utf-8");
9664
9853
  if (content.includes("ignoreDuringBuilds")) return;
@@ -9666,7 +9855,7 @@ async function patchNextConfigForExport(outRoot) {
9666
9855
  /(const\s+nextConfig\s*(?::\s*\w+)?\s*=\s*\{)/,
9667
9856
  "$1\n eslint: { ignoreDuringBuilds: true },\n typescript: { ignoreBuildErrors: true },"
9668
9857
  );
9669
- await writeFile4(p, content, "utf-8");
9858
+ await writeFile5(p, content, "utf-8");
9670
9859
  return;
9671
9860
  }
9672
9861
  }
@@ -9700,13 +9889,13 @@ EXPOSE 3000
9700
9889
  \`\`\`
9701
9890
  `;
9702
9891
  async function ensureReadmeDeploySection(outRoot) {
9703
- const readmePath = join12(outRoot, "README.md");
9892
+ const readmePath = join13(outRoot, "README.md");
9704
9893
  if (!existsSync20(readmePath)) return;
9705
9894
  try {
9706
9895
  let content = await readFile6(readmePath, "utf-8");
9707
9896
  if (/##\s+Deploy\b/m.test(content)) return;
9708
9897
  content = content.trimEnd() + DEPLOY_SECTION + "\n";
9709
- await writeFile4(readmePath, content);
9898
+ await writeFile5(readmePath, content);
9710
9899
  } catch {
9711
9900
  }
9712
9901
  }
@@ -9720,22 +9909,22 @@ async function countPages(outRoot) {
9720
9909
  return;
9721
9910
  }
9722
9911
  for (const e of entries) {
9723
- const full = join12(dir, e.name);
9912
+ const full = join13(dir, e.name);
9724
9913
  if (e.isFile() && e.name === "page.tsx") n++;
9725
9914
  else if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "api") await walk(full);
9726
9915
  }
9727
9916
  }
9728
- const appDir = join12(outRoot, "app");
9917
+ const appDir = join13(outRoot, "app");
9729
9918
  if (existsSync20(appDir)) await walk(appDir);
9730
9919
  return n;
9731
9920
  }
9732
9921
  function countComponents(outRoot) {
9733
9922
  let n = 0;
9734
9923
  for (const sub of ["ui", "shared"]) {
9735
- const dir = join12(outRoot, "components", sub);
9924
+ const dir = join13(outRoot, "components", sub);
9736
9925
  if (!existsSync20(dir)) continue;
9737
9926
  try {
9738
- n += readdirSync3(dir).filter((f) => f.endsWith(".tsx") || f.endsWith(".jsx")).length;
9927
+ n += readdirSync4(dir).filter((f) => f.endsWith(".tsx") || f.endsWith(".jsx")).length;
9739
9928
  } catch {
9740
9929
  }
9741
9930
  }
@@ -9748,7 +9937,7 @@ async function collectImportedPackages2(dir, extensions) {
9748
9937
  async function walk(d) {
9749
9938
  const entries = await readdir3(d, { withFileTypes: true });
9750
9939
  for (const e of entries) {
9751
- const full = join12(d, e.name);
9940
+ const full = join13(d, e.name);
9752
9941
  if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules") {
9753
9942
  await walk(full);
9754
9943
  continue;
@@ -9771,7 +9960,7 @@ async function collectImportedPackages2(dir, extensions) {
9771
9960
  return packages;
9772
9961
  }
9773
9962
  async function findMissingDepsInExport(outRoot) {
9774
- const pkgPath = join12(outRoot, "package.json");
9963
+ const pkgPath = join13(outRoot, "package.json");
9775
9964
  if (!existsSync20(pkgPath)) return [];
9776
9965
  let pkg;
9777
9966
  try {
@@ -9780,7 +9969,7 @@ async function findMissingDepsInExport(outRoot) {
9780
9969
  return [];
9781
9970
  }
9782
9971
  const inDeps = /* @__PURE__ */ new Set([...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})]);
9783
- const codeDirs = [join12(outRoot, "app"), join12(outRoot, "components")];
9972
+ const codeDirs = [join13(outRoot, "app"), join13(outRoot, "components")];
9784
9973
  const extensions = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx"]);
9785
9974
  const imported = /* @__PURE__ */ new Set();
9786
9975
  for (const dir of codeDirs) {
@@ -9792,33 +9981,33 @@ async function findMissingDepsInExport(outRoot) {
9792
9981
  async function stripCoherentArtifacts(outputDir) {
9793
9982
  const removed = [];
9794
9983
  for (const p of ["app/design-system", "app/api/design-system"]) {
9795
- const full = join12(outputDir, p);
9984
+ const full = join13(outputDir, p);
9796
9985
  if (existsSync20(full)) {
9797
9986
  rmSync4(full, { recursive: true, force: true });
9798
9987
  removed.push(p);
9799
9988
  }
9800
9989
  }
9801
- const appNavPath = join12(outputDir, "app", "AppNav.tsx");
9990
+ const appNavPath = join13(outputDir, "app", "AppNav.tsx");
9802
9991
  if (existsSync20(appNavPath)) {
9803
9992
  rmSync4(appNavPath, { force: true });
9804
9993
  removed.push("app/AppNav.tsx");
9805
9994
  }
9806
- const layoutPath = join12(outputDir, "app", "layout.tsx");
9995
+ const layoutPath = join13(outputDir, "app", "layout.tsx");
9807
9996
  if (existsSync20(layoutPath)) {
9808
9997
  let layout = await readFile6(layoutPath, "utf-8");
9809
9998
  layout = layout.replace(/import\s*\{?\s*AppNav\s*\}?\s*from\s*['"][^'"]+['"]\s*\n?/g, "");
9810
9999
  layout = layout.replace(/\s*<AppNav\s*\/?\s*>\s*/g, "\n");
9811
- await writeFile4(layoutPath, layout, "utf-8");
10000
+ await writeFile5(layoutPath, layout, "utf-8");
9812
10001
  }
9813
- const sharedHeaderPath = join12(outputDir, "components", "shared", "header.tsx");
10002
+ const sharedHeaderPath = join13(outputDir, "components", "shared", "header.tsx");
9814
10003
  if (existsSync20(sharedHeaderPath)) {
9815
10004
  let header = await readFile6(sharedHeaderPath, "utf-8");
9816
10005
  header = header.replace(/<Link\s[^>]*href="\/design-system"[^>]*>[\s\S]*?<\/Link>/g, "");
9817
10006
  header = header.replace(/\n\s*<>\s*\n/, "\n");
9818
10007
  header = header.replace(/\n\s*<\/>\s*\n/, "\n");
9819
- await writeFile4(sharedHeaderPath, header, "utf-8");
10008
+ await writeFile5(sharedHeaderPath, header, "utf-8");
9820
10009
  }
9821
- const guardPath = join12(outputDir, "app", "ShowWhenNotAuthRoute.tsx");
10010
+ const guardPath = join13(outputDir, "app", "ShowWhenNotAuthRoute.tsx");
9822
10011
  if (existsSync20(guardPath)) {
9823
10012
  let guard = await readFile6(guardPath, "utf-8");
9824
10013
  guard = guard.replace(/['"],?\s*'\/design-system['"],?\s*/g, "");
@@ -9832,10 +10021,10 @@ async function stripCoherentArtifacts(outputDir) {
9832
10021
  layout = layout.replace(/import\s+\w+\s+from\s*['"]\.\/ShowWhenNotAuthRoute['"]\s*\n?/g, "");
9833
10022
  layout = layout.replace(/\s*<ShowWhenNotAuthRoute>\s*\n?/g, "\n");
9834
10023
  layout = layout.replace(/\s*<\/ShowWhenNotAuthRoute>\s*\n?/g, "\n");
9835
- await writeFile4(layoutPath, layout, "utf-8");
10024
+ await writeFile5(layoutPath, layout, "utf-8");
9836
10025
  }
9837
10026
  } else {
9838
- await writeFile4(guardPath, guard, "utf-8");
10027
+ await writeFile5(guardPath, guard, "utf-8");
9839
10028
  }
9840
10029
  }
9841
10030
  for (const name of [
@@ -9847,14 +10036,14 @@ async function stripCoherentArtifacts(outputDir) {
9847
10036
  ".env.local",
9848
10037
  "recommendations.md"
9849
10038
  ]) {
9850
- const full = join12(outputDir, name);
10039
+ const full = join13(outputDir, name);
9851
10040
  if (existsSync20(full)) {
9852
10041
  rmSync4(full, { force: true });
9853
10042
  removed.push(name);
9854
10043
  }
9855
10044
  }
9856
10045
  for (const dir of [".claude", ".coherent"]) {
9857
- const full = join12(outputDir, dir);
10046
+ const full = join13(outputDir, dir);
9858
10047
  if (existsSync20(full)) {
9859
10048
  rmSync4(full, { recursive: true, force: true });
9860
10049
  removed.push(dir + "/");
@@ -10054,8 +10243,8 @@ async function regenerateDocsCommand() {
10054
10243
 
10055
10244
  // src/commands/fix.ts
10056
10245
  import chalk18 from "chalk";
10057
- import { readdirSync as readdirSync4, readFileSync as readFileSync14, existsSync as existsSync21, writeFileSync as writeFileSync11, rmSync as rmSync5, mkdirSync as mkdirSync7 } from "fs";
10058
- import { resolve as resolve12, join as join13 } from "path";
10246
+ import { readdirSync as readdirSync5, readFileSync as readFileSync14, existsSync as existsSync21, writeFileSync as writeFileSync11, rmSync as rmSync5, mkdirSync as mkdirSync7 } from "fs";
10247
+ import { resolve as resolve12, join as join14 } from "path";
10059
10248
  import {
10060
10249
  DesignSystemManager as DesignSystemManager11,
10061
10250
  ComponentManager as ComponentManager5,
@@ -10079,9 +10268,9 @@ function extractComponentIdsFromCode2(code) {
10079
10268
  function listTsxFiles(dir) {
10080
10269
  const files = [];
10081
10270
  try {
10082
- const entries = readdirSync4(dir, { withFileTypes: true });
10271
+ const entries = readdirSync5(dir, { withFileTypes: true });
10083
10272
  for (const e of entries) {
10084
- const full = join13(dir, e.name);
10273
+ const full = join14(dir, e.name);
10085
10274
  if (e.isDirectory() && e.name !== "node_modules" && !e.name.startsWith(".")) {
10086
10275
  files.push(...listTsxFiles(full));
10087
10276
  } else if (e.isFile() && e.name.endsWith(".tsx")) {
@@ -10110,7 +10299,7 @@ async function fixCommand(opts = {}) {
10110
10299
  console.log(chalk18.cyan("\ncoherent fix\n"));
10111
10300
  }
10112
10301
  if (!skipCache) {
10113
- const nextDir = join13(projectRoot, ".next");
10302
+ const nextDir = join14(projectRoot, ".next");
10114
10303
  if (existsSync21(nextDir)) {
10115
10304
  if (!dryRun) rmSync5(nextDir, { recursive: true, force: true });
10116
10305
  fixes.push("Cleared build cache");
@@ -10367,13 +10556,13 @@ async function fixCommand(opts = {}) {
10367
10556
  // src/commands/check.ts
10368
10557
  import chalk19 from "chalk";
10369
10558
  import { resolve as resolve13 } from "path";
10370
- import { readdirSync as readdirSync5, readFileSync as readFileSync15, statSync as statSync2, existsSync as existsSync22 } from "fs";
10559
+ import { readdirSync as readdirSync6, readFileSync as readFileSync15, statSync as statSync2, existsSync as existsSync22 } from "fs";
10371
10560
  import { loadManifest as loadManifest10 } from "@getcoherent/core";
10372
10561
  var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["node_modules", "design-system"]);
10373
10562
  function findTsxFiles(dir) {
10374
10563
  const results = [];
10375
10564
  try {
10376
- const entries = readdirSync5(dir);
10565
+ const entries = readdirSync6(dir);
10377
10566
  for (const entry of entries) {
10378
10567
  const full = resolve13(dir, entry);
10379
10568
  const stat = statSync2(full);
@@ -10701,8 +10890,8 @@ import { existsSync as existsSync23 } from "fs";
10701
10890
  import { resolve as resolve14 } from "path";
10702
10891
 
10703
10892
  // src/utils/ds-files.ts
10704
- import { mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
10705
- import { join as join14, dirname as dirname8 } from "path";
10893
+ import { mkdir as mkdir6, writeFile as writeFile6 } from "fs/promises";
10894
+ import { join as join15, dirname as dirname8 } from "path";
10706
10895
  import { DesignSystemGenerator } from "@getcoherent/core";
10707
10896
  var SHARED_DS_KEYS = [
10708
10897
  "app/design-system/shared/page.tsx",
@@ -10716,9 +10905,9 @@ async function writeDesignSystemFiles(projectRoot, config2, options) {
10716
10905
  const toWrite = options?.sharedOnly ? new Map([...files].filter(([path3]) => SHARED_DS_KEYS.includes(path3))) : files;
10717
10906
  const written = [];
10718
10907
  for (const [relativePath, content] of toWrite) {
10719
- const fullPath = join14(projectRoot, relativePath);
10720
- await mkdir5(dirname8(fullPath), { recursive: true });
10721
- await writeFile5(fullPath, content, "utf-8");
10908
+ const fullPath = join15(projectRoot, relativePath);
10909
+ await mkdir6(dirname8(fullPath), { recursive: true });
10910
+ await writeFile6(fullPath, content, "utf-8");
10722
10911
  written.push(relativePath);
10723
10912
  }
10724
10913
  return written;
@@ -10854,8 +11043,8 @@ function createComponentsCommand() {
10854
11043
  // src/commands/import-cmd.ts
10855
11044
  import chalk26 from "chalk";
10856
11045
  import ora6 from "ora";
10857
- import { writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
10858
- import { resolve as resolve15, join as join15, dirname as dirname9 } from "path";
11046
+ import { writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
11047
+ import { resolve as resolve15, join as join16, dirname as dirname9 } from "path";
10859
11048
  import { existsSync as existsSync24 } from "fs";
10860
11049
  import {
10861
11050
  FigmaClient,
@@ -11003,9 +11192,9 @@ async function importFigmaAction(urlOrKey, opts) {
11003
11192
  stats.filesWritten.push(filePath);
11004
11193
  return;
11005
11194
  }
11006
- const fullPath = join15(projectRoot, filePath);
11007
- await mkdir6(dirname9(fullPath), { recursive: true });
11008
- await writeFile6(fullPath, content, "utf-8");
11195
+ const fullPath = join16(projectRoot, filePath);
11196
+ await mkdir7(dirname9(fullPath), { recursive: true });
11197
+ await writeFile7(fullPath, content, "utf-8");
11009
11198
  stats.filesWritten.push(filePath);
11010
11199
  };
11011
11200
  try {
@@ -11079,7 +11268,7 @@ async function importFigmaAction(urlOrKey, opts) {
11079
11268
  });
11080
11269
  if (dryRun) stats.filesWritten.push(FIGMA_COMPONENT_MAP_FILENAME);
11081
11270
  else
11082
- await writeFile6(
11271
+ await writeFile7(
11083
11272
  resolve15(projectRoot, FIGMA_COMPONENT_MAP_FILENAME),
11084
11273
  JSON.stringify(componentMapObj, null, 2),
11085
11274
  "utf-8"
@@ -11119,7 +11308,7 @@ async function importFigmaAction(urlOrKey, opts) {
11119
11308
  });
11120
11309
  await dsm.save();
11121
11310
  } else {
11122
- await writeFile6(
11311
+ await writeFile7(
11123
11312
  configPath,
11124
11313
  `/**
11125
11314
  * Design System Configuration
@@ -11134,10 +11323,10 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
11134
11323
  stats.configUpdated = true;
11135
11324
  spinner.succeed("design-system.config.ts updated");
11136
11325
  spinner.start("Ensuring root layout...");
11137
- const layoutPath = join15(projectRoot, "app/layout.tsx");
11326
+ const layoutPath = join16(projectRoot, "app/layout.tsx");
11138
11327
  if (!existsSync24(layoutPath)) {
11139
- await mkdir6(dirname9(layoutPath), { recursive: true });
11140
- await writeFile6(layoutPath, MINIMAL_ROOT_LAYOUT, "utf-8");
11328
+ await mkdir7(dirname9(layoutPath), { recursive: true });
11329
+ await writeFile7(layoutPath, MINIMAL_ROOT_LAYOUT, "utf-8");
11141
11330
  stats.filesWritten.push("app/layout.tsx");
11142
11331
  }
11143
11332
  spinner.succeed("Root layout OK");
@@ -11226,7 +11415,7 @@ async function dsRegenerateCommand() {
11226
11415
  import chalk28 from "chalk";
11227
11416
  import ora8 from "ora";
11228
11417
  import { readFileSync as readFileSync16, existsSync as existsSync25 } from "fs";
11229
- import { join as join16 } from "path";
11418
+ import { join as join17 } from "path";
11230
11419
  import { DesignSystemManager as DesignSystemManager15, CLI_VERSION as CLI_VERSION4 } from "@getcoherent/core";
11231
11420
 
11232
11421
  // src/utils/migrations.ts
@@ -11395,7 +11584,7 @@ var EXPECTED_CSS_VARS = [
11395
11584
  "--sidebar-ring"
11396
11585
  ];
11397
11586
  function checkMissingCssVars(projectRoot) {
11398
- const globalsPath = join16(projectRoot, "app", "globals.css");
11587
+ const globalsPath = join17(projectRoot, "app", "globals.css");
11399
11588
  if (!existsSync25(globalsPath)) return [];
11400
11589
  try {
11401
11590
  const content = readFileSync16(globalsPath, "utf-8");
@@ -11405,7 +11594,7 @@ function checkMissingCssVars(projectRoot) {
11405
11594
  }
11406
11595
  }
11407
11596
  function patchGlobalsCss(projectRoot, missingVars) {
11408
- const globalsPath = join16(projectRoot, "app", "globals.css");
11597
+ const globalsPath = join17(projectRoot, "app", "globals.css");
11409
11598
  if (!existsSync25(globalsPath) || missingVars.length === 0) return;
11410
11599
  const { writeFileSync: writeFileSync13 } = __require("fs");
11411
11600
  let content = readFileSync16(globalsPath, "utf-8");
@@ -11487,14 +11676,14 @@ async function undoCommand(options) {
11487
11676
  import chalk30 from "chalk";
11488
11677
  import ora9 from "ora";
11489
11678
  import { existsSync as existsSync26, readFileSync as readFileSync17 } from "fs";
11490
- import { join as join17, relative as relative4, dirname as dirname10 } from "path";
11679
+ import { join as join18, relative as relative5, dirname as dirname10 } from "path";
11491
11680
  import { readdir as readdir4, readFile as readFile7 } from "fs/promises";
11492
11681
  import { DesignSystemManager as DesignSystemManager16 } from "@getcoherent/core";
11493
11682
  import { loadManifest as loadManifest12, saveManifest as saveManifest5, findSharedComponent } from "@getcoherent/core";
11494
11683
  function extractTokensFromProject(projectRoot) {
11495
11684
  const lightColors = {};
11496
11685
  const darkColors = {};
11497
- const globalsPath = join17(projectRoot, "app", "globals.css");
11686
+ const globalsPath = join18(projectRoot, "app", "globals.css");
11498
11687
  if (existsSync26(globalsPath)) {
11499
11688
  const css = readFileSync17(globalsPath, "utf-8");
11500
11689
  const rootMatch = css.match(/:root\s*\{([^}]+)\}/s);
@@ -11502,7 +11691,7 @@ function extractTokensFromProject(projectRoot) {
11502
11691
  const darkMatch = css.match(/\.dark\s*\{([^}]+)\}/s);
11503
11692
  if (darkMatch) parseVarsInto(darkMatch[1], darkColors);
11504
11693
  }
11505
- const layoutPath = join17(projectRoot, "app", "layout.tsx");
11694
+ const layoutPath = join18(projectRoot, "app", "layout.tsx");
11506
11695
  let layoutCode = "";
11507
11696
  if (existsSync26(layoutPath)) {
11508
11697
  layoutCode = readFileSync17(layoutPath, "utf-8");
@@ -11546,14 +11735,14 @@ function parseVarsInto(block, target) {
11546
11735
  }
11547
11736
  async function detectCustomComponents(projectRoot, allPageCode) {
11548
11737
  const results = [];
11549
- const componentsDir = join17(projectRoot, "components");
11738
+ const componentsDir = join18(projectRoot, "components");
11550
11739
  if (!existsSync26(componentsDir)) return results;
11551
11740
  const files = [];
11552
11741
  await walkForTsx(componentsDir, files, ["ui"]);
11553
11742
  const fileResults = await Promise.all(
11554
11743
  files.map(async (filePath) => {
11555
11744
  const code = await readFile7(filePath, "utf-8");
11556
- const relFile = relative4(projectRoot, filePath);
11745
+ const relFile = relative5(projectRoot, filePath);
11557
11746
  const exportedNames = extractExportedComponentNames2(code);
11558
11747
  return exportedNames.map((name) => ({
11559
11748
  name,
@@ -11574,7 +11763,7 @@ async function walkForTsx(dir, files, skipDirs) {
11574
11763
  return;
11575
11764
  }
11576
11765
  for (const e of entries) {
11577
- const full = join17(dir, e.name);
11766
+ const full = join18(dir, e.name);
11578
11767
  if (e.isDirectory()) {
11579
11768
  if (skipDirs.includes(e.name) || e.name.startsWith(".")) continue;
11580
11769
  await walkForTsx(full, files, skipDirs);
@@ -11648,14 +11837,14 @@ async function discoverPages(appDir) {
11648
11837
  return;
11649
11838
  }
11650
11839
  for (const entry of entries) {
11651
- const full = join17(dir, entry.name);
11840
+ const full = join18(dir, entry.name);
11652
11841
  if (entry.isDirectory()) {
11653
11842
  if (["design-system", "api", "_not-found"].includes(entry.name)) continue;
11654
11843
  if (entry.name.startsWith(".")) continue;
11655
11844
  await walk(full);
11656
11845
  } else if (entry.name === "page.tsx" || entry.name === "page.jsx") {
11657
11846
  const code = await readFile7(full, "utf-8");
11658
- const routeDir = dirname10(relative4(appDir, full));
11847
+ const routeDir = dirname10(relative5(appDir, full));
11659
11848
  let route = routeDir === "." ? "/" : "/" + routeDir;
11660
11849
  route = route.replace(/\/\([^)]+\)/g, "");
11661
11850
  if (!route.startsWith("/")) route = "/" + route;
@@ -11725,7 +11914,7 @@ async function syncCommand(options = {}) {
11725
11914
  if (dryRun) console.log(chalk30.yellow(" [dry-run] No files will be written\n"));
11726
11915
  const spinner = ora9("Scanning project files...").start();
11727
11916
  try {
11728
- const appDir = join17(project.root, "app");
11917
+ const appDir = join18(project.root, "app");
11729
11918
  if (!existsSync26(appDir)) {
11730
11919
  spinner.fail("No app/ directory found");
11731
11920
  process.exit(1);
@@ -11952,14 +12141,14 @@ async function syncCommand(options = {}) {
11952
12141
 
11953
12142
  // src/utils/update-notifier.ts
11954
12143
  import { existsSync as existsSync27, mkdirSync as mkdirSync8, readFileSync as readFileSync18, writeFileSync as writeFileSync12 } from "fs";
11955
- import { join as join18 } from "path";
12144
+ import { join as join19 } from "path";
11956
12145
  import { homedir } from "os";
11957
12146
  import chalk31 from "chalk";
11958
12147
  import { CLI_VERSION as CLI_VERSION5 } from "@getcoherent/core";
11959
12148
  var DEBUG5 = process.env.COHERENT_DEBUG === "1";
11960
12149
  var PACKAGE_NAME = "@getcoherent/cli";
11961
- var CACHE_DIR = join18(homedir(), ".coherent");
11962
- var CACHE_FILE = join18(CACHE_DIR, "update-check.json");
12150
+ var CACHE_DIR = join19(homedir(), ".coherent");
12151
+ var CACHE_FILE = join19(CACHE_DIR, "update-check.json");
11963
12152
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
11964
12153
  function readCache() {
11965
12154
  try {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.5.2",
6
+ "version": "0.5.3",
7
7
  "description": "CLI interface for Coherent Design Method",
8
8
  "type": "module",
9
9
  "main": "./dist/index.js",
@@ -43,7 +43,7 @@
43
43
  "ora": "^7.0.1",
44
44
  "prompts": "^2.4.2",
45
45
  "zod": "^3.22.4",
46
- "@getcoherent/core": "0.5.2"
46
+ "@getcoherent/core": "0.5.3"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/node": "^20.11.0",