@getcoherent/cli 0.5.13 → 0.5.15

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.
package/dist/index.js CHANGED
@@ -1227,9 +1227,7 @@ var COMPONENT_APIS = {
1227
1227
  </CommandGroup>
1228
1228
  </CommandList>
1229
1229
  </Command>`,
1230
- antiPatterns: [
1231
- "NEVER build custom search palette \u2014 use Command/CommandDialog"
1232
- ]
1230
+ antiPatterns: ["NEVER build custom search palette \u2014 use Command/CommandDialog"]
1233
1231
  },
1234
1232
  tabs: {
1235
1233
  name: "Tabs",
@@ -1269,7 +1267,16 @@ var COMPONENT_APIS = {
1269
1267
  },
1270
1268
  table: {
1271
1269
  name: "Table",
1272
- subcomponents: ["Table", "TableHeader", "TableBody", "TableFooter", "TableRow", "TableHead", "TableCell", "TableCaption"],
1270
+ subcomponents: [
1271
+ "Table",
1272
+ "TableHeader",
1273
+ "TableBody",
1274
+ "TableFooter",
1275
+ "TableRow",
1276
+ "TableHead",
1277
+ "TableCell",
1278
+ "TableCaption"
1279
+ ],
1273
1280
  importPath: "@/components/ui/table",
1274
1281
  keyProps: {},
1275
1282
  usage: `<Table>
@@ -1280,9 +1287,7 @@ var COMPONENT_APIS = {
1280
1287
  <TableRow><TableCell>Value</TableCell></TableRow>
1281
1288
  </TableBody>
1282
1289
  </Table>`,
1283
- antiPatterns: [
1284
- "NEVER use native <table> \u2014 use shadcn Table for consistent styling"
1285
- ]
1290
+ antiPatterns: ["NEVER use native <table> \u2014 use shadcn Table for consistent styling"]
1286
1291
  },
1287
1292
  accordion: {
1288
1293
  name: "Accordion",
@@ -1349,17 +1354,15 @@ var ShadcnProvider = class {
1349
1354
  if (!force && deps.existsSync(componentPath)) return;
1350
1355
  try {
1351
1356
  await new Promise((resolve16, reject) => {
1352
- deps.exec(
1353
- `npx shadcn@latest add ${name} --yes --overwrite`,
1354
- { cwd: projectRoot, timeout: 15e3 },
1355
- (err) => {
1356
- if (err) reject(err);
1357
- else resolve16();
1358
- }
1359
- );
1357
+ deps.exec(`npx shadcn@latest add ${name} --yes --overwrite`, { cwd: projectRoot, timeout: 15e3 }, (err) => {
1358
+ if (err) reject(err);
1359
+ else resolve16();
1360
+ });
1360
1361
  });
1361
1362
  } catch {
1362
- console.warn(`Could not install ${name} (network error or timeout). Run \`npx shadcn@latest add ${name}\` manually.`);
1363
+ console.warn(
1364
+ `Could not install ${name} (network error or timeout). Run \`npx shadcn@latest add ${name}\` manually.`
1365
+ );
1363
1366
  }
1364
1367
  }
1365
1368
  async installComponent(id, projectRoot, options) {
@@ -1452,10 +1455,7 @@ var ShadcnProvider = class {
1452
1455
  "ring"
1453
1456
  ];
1454
1457
  const lines = sidebarVars.map((v) => ` --color-sidebar-${v}: var(--sidebar-${v});`);
1455
- const chartLines = Array.from(
1456
- { length: 5 },
1457
- (_, i) => ` --color-chart-${i + 1}: var(--chart-${i + 1});`
1458
- );
1458
+ const chartLines = Array.from({ length: 5 }, (_, i) => ` --color-chart-${i + 1}: var(--chart-${i + 1});`);
1459
1459
  return `@theme inline {
1460
1460
  ${lines.join("\n")}
1461
1461
  ${chartLines.join("\n")}
@@ -1678,7 +1678,8 @@ function createMinimalConfig() {
1678
1678
  framework: "next",
1679
1679
  typescript: true,
1680
1680
  cssFramework: "tailwind",
1681
- autoScaffold: false
1681
+ autoScaffold: false,
1682
+ homePagePlaceholder: true
1682
1683
  },
1683
1684
  createdAt: now,
1684
1685
  updatedAt: now
@@ -3747,14 +3748,14 @@ async function createAppRouteGroupLayout(projectPath) {
3747
3748
  import chalk13 from "chalk";
3748
3749
  import ora2 from "ora";
3749
3750
  import { resolve as resolve9, relative as relative2, join as join11 } from "path";
3750
- import { existsSync as existsSync16, readFileSync as readFileSync10, mkdirSync as mkdirSync6, readdirSync as readdirSync2 } from "fs";
3751
+ import { existsSync as existsSync16, readFileSync as readFileSync11, mkdirSync as mkdirSync6, readdirSync as readdirSync3 } from "fs";
3751
3752
  import {
3752
3753
  DesignSystemManager as DesignSystemManager7,
3753
- ComponentManager as ComponentManager4,
3754
+ ComponentManager as ComponentManager5,
3754
3755
  PageManager as PageManager3,
3755
3756
  CLI_VERSION as CLI_VERSION2,
3756
3757
  getTemplateForPageType as getTemplateForPageType2,
3757
- loadManifest as loadManifest7,
3758
+ loadManifest as loadManifest8,
3758
3759
  saveManifest as saveManifest2
3759
3760
  } from "@getcoherent/core";
3760
3761
 
@@ -3831,7 +3832,7 @@ Please set ${envVar} in your environment or .env file.`);
3831
3832
  }
3832
3833
  if (preferredProvider === "openai") {
3833
3834
  try {
3834
- const { OpenAIClient } = await import("./openai-provider-3XC6CVZR.js");
3835
+ const { OpenAIClient } = await import("./openai-provider-FSXSVEYD.js");
3835
3836
  return await OpenAIClient.create(apiKey2, config2?.model);
3836
3837
  } catch (error) {
3837
3838
  if (error.message?.includes("not installed")) {
@@ -3845,7 +3846,7 @@ Error: ${error.message}`
3845
3846
  );
3846
3847
  }
3847
3848
  } else {
3848
- const { ClaudeClient } = await import("./claude-QH2XB2E3.js");
3849
+ const { ClaudeClient } = await import("./claude-RFHVT7RC.js");
3849
3850
  return ClaudeClient.create(apiKey2, config2?.model);
3850
3851
  }
3851
3852
  }
@@ -3858,7 +3859,7 @@ Error: ${error.message}`
3858
3859
  switch (provider) {
3859
3860
  case "openai":
3860
3861
  try {
3861
- const { OpenAIClient } = await import("./openai-provider-3XC6CVZR.js");
3862
+ const { OpenAIClient } = await import("./openai-provider-FSXSVEYD.js");
3862
3863
  return await OpenAIClient.create(apiKey, config2?.model);
3863
3864
  } catch (error) {
3864
3865
  if (error.message?.includes("not installed")) {
@@ -3872,7 +3873,7 @@ Error: ${error.message}`
3872
3873
  );
3873
3874
  }
3874
3875
  case "claude":
3875
- const { ClaudeClient } = await import("./claude-QH2XB2E3.js");
3876
+ const { ClaudeClient } = await import("./claude-RFHVT7RC.js");
3876
3877
  return ClaudeClient.create(apiKey, config2?.model);
3877
3878
  default:
3878
3879
  throw new Error(`Unsupported AI provider: ${provider}`);
@@ -3911,6 +3912,27 @@ var PAGE_TEMPLATES = {
3911
3912
  "Label"
3912
3913
  ]
3913
3914
  },
3915
+ register: {
3916
+ description: "Registration page with centered card form",
3917
+ sections: [
3918
+ 'Centered layout: outer div className="flex min-h-svh flex-col items-center justify-center p-6 md:p-10". Inner div className="w-full max-w-sm".',
3919
+ 'Card with CardHeader: CardTitle "Create an account" (text-2xl font-bold), CardDescription "Enter your details to get started" (text-sm text-muted-foreground).',
3920
+ 'CardContent with form: name Input, email Input (type="email"), password Input (type="password"), confirm password Input (type="password"), and a Button "Create account" (w-full).',
3921
+ 'CardFooter: text "Already have an account?" with a Sign in link. All text is text-sm text-muted-foreground.',
3922
+ 'This page uses "use client" (has useState for form state). Do NOT include export const metadata.'
3923
+ ],
3924
+ components: [
3925
+ "Card",
3926
+ "CardHeader",
3927
+ "CardTitle",
3928
+ "CardDescription",
3929
+ "CardContent",
3930
+ "CardFooter",
3931
+ "Button",
3932
+ "Input",
3933
+ "Label"
3934
+ ]
3935
+ },
3914
3936
  pricing: {
3915
3937
  description: "Pricing page with tier comparison cards",
3916
3938
  sections: [
@@ -4037,6 +4059,43 @@ var PAGE_TEMPLATES = {
4037
4059
  "Page header. Timeline: each version with version badge, date, list of entries (type: text). Border-left timeline pattern. Badge component for version/date."
4038
4060
  ],
4039
4061
  components: ["Badge"]
4062
+ },
4063
+ team: {
4064
+ description: "Team page with member cards",
4065
+ sections: [
4066
+ 'Page header: h1 "Our Team" className="text-2xl font-bold tracking-tight" + p className="text-sm text-muted-foreground"',
4067
+ 'Grid of team member Cards (className="grid gap-4 md:grid-cols-2 lg:grid-cols-4"). Each card: avatar placeholder (bg-muted rounded-full size-12 with initials), name (text-sm font-semibold), role (text-sm text-muted-foreground).'
4068
+ ],
4069
+ components: ["Card", "CardContent"]
4070
+ },
4071
+ tasks: {
4072
+ description: "Task list page with status badges and search",
4073
+ sections: [
4074
+ 'Page header: h1 "Tasks" + description. Search input with Search icon.',
4075
+ "Task list: divide-y container. Each row: status badge (colored), task title, priority badge, assignee name. Use flex items-center justify-between.",
4076
+ 'This page uses "use client" (has useState for search). Do NOT include export const metadata.'
4077
+ ],
4078
+ components: ["Input", "Badge"]
4079
+ },
4080
+ "task-detail": {
4081
+ description: "Task detail page with info and activity",
4082
+ sections: [
4083
+ "Back button linking to /tasks. Page header with task title and description.",
4084
+ "Two-column layout (md:grid-cols-2): Left Card with task details (status, priority, assignee, due date). Right Card with activity timeline.",
4085
+ 'This page uses "use client" (has useState). Do NOT include export const metadata.'
4086
+ ],
4087
+ components: ["Card", "CardHeader", "CardTitle", "CardContent", "Button"]
4088
+ },
4089
+ "reset-password": {
4090
+ description: "Reset password page with centered card form",
4091
+ sections: [
4092
+ 'Centered layout: outer div className="flex min-h-svh flex-col items-center justify-center p-6 md:p-10". Inner div className="w-full max-w-sm".',
4093
+ 'Card with CardHeader: CardTitle "Reset Password" (text-xl), CardDescription.',
4094
+ 'CardContent with form: new password Input (type="password"), confirm password Input (type="password"), Button "Reset password" (w-full).',
4095
+ 'Footer text: "Remember your password?" with Sign in link.',
4096
+ 'This page uses "use client" (has useState for form state). Do NOT include export const metadata.'
4097
+ ],
4098
+ components: ["Card", "CardHeader", "CardTitle", "CardDescription", "CardContent", "Button", "Input", "Label"]
4040
4099
  }
4041
4100
  };
4042
4101
  var AUTH_ROUTE_SEGMENTS = /* @__PURE__ */ new Set([
@@ -4057,8 +4116,9 @@ function detectPageType(pageName) {
4057
4116
  const normalized = pageName.toLowerCase();
4058
4117
  if (/dashboard|admin|overview/.test(normalized)) return "dashboard";
4059
4118
  if (/login|signin|sign-in/.test(normalized)) return "login";
4119
+ if (/register|signup|sign.?up/.test(normalized)) return "register";
4060
4120
  if (/pricing|plans|subscription/.test(normalized)) return "pricing";
4061
- if (/about|team|company/.test(normalized)) return "about";
4121
+ if (/about|company/.test(normalized)) return "about";
4062
4122
  if (/contact|support|help/.test(normalized)) return "contact";
4063
4123
  if (/settings|preferences|account/.test(normalized)) return "settings";
4064
4124
  if (/home|landing|hero/.test(normalized)) return "landing";
@@ -4069,6 +4129,10 @@ function detectPageType(pageName) {
4069
4129
  if (/gallery|portfolio|images/.test(normalized)) return "gallery";
4070
4130
  if (/faq|frequently|questions/.test(normalized)) return "faq";
4071
4131
  if (/changelog|release|versions/.test(normalized)) return "changelog";
4132
+ if (/team|members/.test(normalized)) return "team";
4133
+ if (/tasks?/.test(normalized) && /detail|\[id\]/.test(normalized)) return "task-detail";
4134
+ if (/tasks?/.test(normalized)) return "tasks";
4135
+ if (/reset.?password/.test(normalized)) return "reset-password";
4072
4136
  return null;
4073
4137
  }
4074
4138
  function expandPageRequest(pageName, userRequest) {
@@ -4154,6 +4218,8 @@ LINKS & INTERACTIVE STATES (consistency is critical):
4154
4218
  - ALL links on the SAME page MUST use the SAME style. Never mix underlined and non-underlined text links.
4155
4219
  - ALL Button variants MUST have: hover: state, focus-visible:ring-2 focus-visible:ring-ring, active: state, disabled:opacity-50.
4156
4220
  - ALL interactive elements MUST have visible hover and focus-visible states.
4221
+ - CRITICAL: Every <Link> MUST have an href prop. Missing href causes runtime errors. Never use <Link className="..."> or <Button asChild><Link> without href.
4222
+ - When shared components exist (@/components/shared/*), ALWAYS import and use them instead of re-implementing similar patterns inline.
4157
4223
 
4158
4224
  ICONS:
4159
4225
  - Size: ALWAYS size-4 (16px). Color: ALWAYS text-muted-foreground. Import: ALWAYS from lucide-react.
@@ -4905,6 +4971,11 @@ async function parseModification(message, context, provider = "auto", options) {
4905
4971
  const navigation = !Array.isArray(raw2) && raw2?.navigation ? raw2.navigation : void 0;
4906
4972
  return { requests: requestsArray2, uxRecommendations: void 0, navigation };
4907
4973
  }
4974
+ if (options?.lightweight) {
4975
+ const raw2 = await ai.parseModification(message);
4976
+ const requestsArray2 = Array.isArray(raw2) ? raw2 : raw2?.requests ?? [];
4977
+ return { requests: requestsArray2, uxRecommendations: void 0 };
4978
+ }
4908
4979
  const componentRegistry = buildComponentRegistry(context.componentManager);
4909
4980
  let enhancedMessage = message;
4910
4981
  let isExpandedPageRequest = false;
@@ -5013,6 +5084,7 @@ For editing an existing shared component use type "modify-layout-block" with tar
5013
5084
  return `You are a design-forward UI architect. Your goal is to create interfaces that are not just functional, but visually distinctive and memorable \u2014 while staying within shadcn/ui and Tailwind CSS.
5014
5085
 
5015
5086
  Parse the user's natural language request into structured modification requests.
5087
+ ${sharedSection}
5016
5088
  ${designThinking}
5017
5089
  ${coreRules}
5018
5090
  ${designQuality}
@@ -5037,7 +5109,6 @@ LINKING RULES (CRITICAL \u2014 prevents broken links):
5037
5109
  - Navigation components should link to ALL existing page routes.
5038
5110
 
5039
5111
  ${componentRegistry}
5040
- ${sharedSection}
5041
5112
 
5042
5113
  Available shadcn/ui components (can be auto-installed): ${availableShadcn.join(", ")}
5043
5114
 
@@ -5264,6 +5335,17 @@ Return valid JSON only, no markdown code fence. Use this shape:
5264
5335
  { "requests": [ ... array of ModificationRequest ... ], "uxRecommendations": "optional markdown or omit key" }
5265
5336
  Legacy: returning only a JSON array of requests is still accepted.`;
5266
5337
  }
5338
+ function buildLightweightPagePrompt(pageName, route, styleContext, sharedComponentsSummary) {
5339
+ return [
5340
+ `Generate complete pageCode for a page called "${pageName}" at route "${route}".`,
5341
+ `Output valid TSX with a default export React component.`,
5342
+ `Use shadcn/ui components (import from @/components/ui/*). Use Tailwind CSS semantic tokens only.`,
5343
+ styleContext ? `Follow this style context:
5344
+ ${styleContext}` : "",
5345
+ sharedComponentsSummary ? `Available shared components:
5346
+ ${sharedComponentsSummary}` : ""
5347
+ ].filter(Boolean).join("\n\n");
5348
+ }
5267
5349
  async function checkComponentReuse(requests, componentManager) {
5268
5350
  const enhanced = [];
5269
5351
  for (const request of requests) {
@@ -6251,6 +6333,17 @@ function validatePageQuality(code, validRoutes) {
6251
6333
  severity: "error"
6252
6334
  });
6253
6335
  }
6336
+ const linkWithoutHrefRe = /<(?:Link|a)\b(?![^>]*\bhref\s*=)[^>]*>/g;
6337
+ let linkNoHrefMatch;
6338
+ while ((linkNoHrefMatch = linkWithoutHrefRe.exec(code)) !== null) {
6339
+ const matchLine = code.slice(0, linkNoHrefMatch.index).split("\n").length;
6340
+ issues.push({
6341
+ line: matchLine,
6342
+ type: "LINK_MISSING_HREF",
6343
+ message: "<Link> or <a> without href prop \u2014 causes Next.js runtime error. Add href attribute.",
6344
+ severity: "error"
6345
+ });
6346
+ }
6254
6347
  issues.push(...detectComponentIssues(code));
6255
6348
  return issues;
6256
6349
  }
@@ -6707,6 +6800,11 @@ ${selectImport}`
6707
6800
  if (fixed !== beforeAsChildFlex) {
6708
6801
  fixes.push("added inline-flex to Button asChild children (base-ui compat)");
6709
6802
  }
6803
+ const beforeLinkHrefFix = fixed;
6804
+ fixed = fixed.replace(/<(Link|a)\b(?![^>]*\bhref\s*=)([^>]*)>/g, '<$1 href="/"$2>');
6805
+ if (fixed !== beforeLinkHrefFix) {
6806
+ fixes.push('added href="/" to <Link>/<a> missing href');
6807
+ }
6710
6808
  const { code: fixedByRules, fixes: ruleFixes } = applyComponentRules(fixed);
6711
6809
  if (ruleFixes.length > 0) {
6712
6810
  fixed = fixedByRules;
@@ -7395,6 +7493,13 @@ function applyDefaults(request) {
7395
7493
  return request;
7396
7494
  }
7397
7495
 
7496
+ // src/commands/chat/split-generator.ts
7497
+ import { z } from "zod";
7498
+ import {
7499
+ loadManifest as loadManifest5,
7500
+ generateSharedComponent as generateSharedComponent2
7501
+ } from "@getcoherent/core";
7502
+
7398
7503
  // src/utils/page-analyzer.ts
7399
7504
  var FORM_COMPONENTS = /* @__PURE__ */ new Set(["Input", "Textarea", "Label", "Select", "Checkbox", "Switch"]);
7400
7505
  var VISUAL_WORDS = /\b(grid lines?|glow|radial|gradient|blur|shadow|overlay|animation|particles?|dots?|vertical|horizontal|decorat|behind|background|divider|spacer|wrapper|container|inner|outer|absolute|relative|translate|opacity|z-index|transition)\b/i;
@@ -7589,9 +7694,27 @@ function parseNavTypeFromPlan(planResult) {
7589
7694
  }
7590
7695
  return "header";
7591
7696
  }
7697
+ function buildSharedComponentsSummary(manifest) {
7698
+ if (manifest.shared.length === 0) return void 0;
7699
+ return manifest.shared.map((e) => {
7700
+ const importPath = e.file.replace(/^components\/shared\//, "").replace(/\.tsx$/, "");
7701
+ const desc = e.description ? ` \u2014 ${e.description}` : "";
7702
+ const propsLine = e.propsInterface ? `
7703
+ Props: ${e.propsInterface}` : "";
7704
+ return ` ${e.id} ${e.name} (${e.type})${desc}
7705
+ Import: @/components/shared/${importPath}${propsLine}`;
7706
+ }).join("\n");
7707
+ }
7708
+ function buildSharedComponentsNote(sharedComponentsSummary) {
7709
+ if (!sharedComponentsSummary) return void 0;
7710
+ return `SHARED COMPONENTS \u2014 MANDATORY REUSE:
7711
+ Before implementing any section, check this list. Import and use matching components from @/components/shared/. Do NOT re-implement these patterns inline.
7712
+
7713
+ ${sharedComponentsSummary}`;
7714
+ }
7592
7715
  async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts) {
7593
7716
  let pageNames = [];
7594
- spinner.start("Phase 1/4 \u2014 Planning pages...");
7717
+ spinner.start("Phase 1/5 \u2014 Planning pages...");
7595
7718
  try {
7596
7719
  const planResult = await parseModification(message, modCtx, provider, { ...parseOpts, planOnly: true });
7597
7720
  const pageReqs = planResult.requests.filter((r) => r.type === "add-page");
@@ -7644,7 +7767,7 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
7644
7767
  const allRoutes = pageNames.map((p) => p.route).join(", ");
7645
7768
  const allPagesList = pageNames.map((p) => `${p.name} (${p.route})`).join(", ");
7646
7769
  const inferredNote = inferred.length > 0 ? ` (${inferred.length} auto-inferred)` : "";
7647
- spinner.succeed(`Phase 1/4 \u2014 Found ${pageNames.length} pages${inferredNote}: ${allPagesList}`);
7770
+ spinner.succeed(`Phase 1/5 \u2014 Found ${pageNames.length} pages${inferredNote}: ${allPagesList}`);
7648
7771
  const homeIdx = pageNames.findIndex((p) => p.route === "/");
7649
7772
  const homePage = homeIdx !== -1 ? pageNames[homeIdx] : pageNames[0];
7650
7773
  const remainingPages = pageNames.filter((_, i) => i !== (homeIdx !== -1 ? homeIdx : 0));
@@ -7652,17 +7775,18 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
7652
7775
  let homeRequest = null;
7653
7776
  let homePageCode = "";
7654
7777
  let reusedExistingAnchor = false;
7655
- if (projectRoot && remainingPages.length > 0) {
7778
+ const isPlaceholder = modCtx.config?.settings?.homePagePlaceholder === true;
7779
+ if (projectRoot && remainingPages.length > 0 && !isPlaceholder) {
7656
7780
  const existingCode = readAnchorPageCodeFromDisk(projectRoot, homePage.route);
7657
7781
  if (existingCode) {
7658
7782
  reusedExistingAnchor = true;
7659
7783
  homePageCode = existingCode;
7660
- spinner.start(`Phase 2/4 \u2014 Loading ${homePage.name} from disk (style anchor)...`);
7661
- spinner.succeed(`Phase 2/4 \u2014 Reused existing ${homePage.name} page (skipped AI regeneration)`);
7784
+ spinner.start(`Phase 2/5 \u2014 Loading ${homePage.name} from disk (style anchor)...`);
7785
+ spinner.succeed(`Phase 2/5 \u2014 Reused existing ${homePage.name} page (skipped AI regeneration)`);
7662
7786
  }
7663
7787
  }
7664
7788
  if (!reusedExistingAnchor) {
7665
- spinner.start(`Phase 2/4 \u2014 Generating ${homePage.name} page (sets design direction)...`);
7789
+ spinner.start(`Phase 2/5 \u2014 Generating ${homePage.name} page (sets design direction)...`);
7666
7790
  try {
7667
7791
  const homeResult = await parseModification(
7668
7792
  `Create ONE page called "${homePage.name}" at route "${homePage.route}". Context: ${message}. This REPLACES the default placeholder page \u2014 generate a complete, content-rich landing page for the project described above. Generate complete pageCode. Include a branded site-wide <header> with navigation links to ALL these pages: ${allPagesList}. Use these EXACT routes in navigation: ${allRoutes}. Include a <footer> at the bottom. Make it visually polished \u2014 this page sets the design direction for the entire site. Do not generate other pages.`,
@@ -7684,22 +7808,42 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
7684
7808
  changes: { id: homePage.id, name: homePage.name, route: homePage.route }
7685
7809
  };
7686
7810
  }
7687
- spinner.succeed(`Phase 2/4 \u2014 ${homePage.name} page generated`);
7811
+ spinner.succeed(`Phase 2/5 \u2014 ${homePage.name} page generated`);
7688
7812
  }
7689
- spinner.start("Phase 3/4 \u2014 Extracting design patterns...");
7813
+ spinner.start("Phase 3/5 \u2014 Extracting design patterns...");
7690
7814
  const styleContext = homePageCode ? extractStyleContext(homePageCode) : "";
7691
7815
  if (styleContext) {
7692
7816
  const lineCount = styleContext.split("\n").length - 1;
7693
7817
  const source = reusedExistingAnchor ? `${homePage.name} (existing file)` : homePage.name;
7694
- spinner.succeed(`Phase 3/4 \u2014 Extracted ${lineCount} style patterns from ${source}`);
7818
+ spinner.succeed(`Phase 3/5 \u2014 Extracted ${lineCount} style patterns from ${source}`);
7695
7819
  } else {
7696
- spinner.succeed("Phase 3/4 \u2014 No style patterns extracted (anchor page had no code)");
7820
+ spinner.succeed("Phase 3/5 \u2014 No style patterns extracted (anchor page had no code)");
7821
+ }
7822
+ if (remainingPages.length >= 2 && homePageCode && projectRoot) {
7823
+ const manifest = await loadManifest5(projectRoot);
7824
+ const shouldSkip = reusedExistingAnchor && manifest.shared.some((e) => e.type !== "layout");
7825
+ if (!shouldSkip) {
7826
+ spinner.start("Phase 3.5/5 \u2014 Extracting shared components...");
7827
+ try {
7828
+ const extraction = await extractSharedComponents(homePageCode, projectRoot, provider ?? "auto");
7829
+ parseOpts.sharedComponentsSummary = extraction.summary;
7830
+ if (extraction.components.length > 0) {
7831
+ const names = extraction.components.map((c) => c.name).join(", ");
7832
+ spinner.succeed(`Phase 3.5/5 \u2014 Extracted ${extraction.components.length} shared components (${names})`);
7833
+ } else {
7834
+ spinner.succeed("Phase 3.5/5 \u2014 No shared components extracted");
7835
+ }
7836
+ } catch {
7837
+ spinner.warn("Phase 3.5/5 \u2014 Could not extract shared components (continuing without)");
7838
+ }
7839
+ }
7697
7840
  }
7698
7841
  if (remainingPages.length === 0) {
7699
7842
  return homeRequest ? [homeRequest] : [];
7700
7843
  }
7701
- spinner.start(`Phase 4/4 \u2014 Generating ${remainingPages.length} pages in parallel...`);
7702
- const sharedNote = "Header and Footer are shared components rendered by the root layout. Do NOT include any site-wide <header>, <nav>, or <footer> in this page. Start with the main content directly.";
7844
+ spinner.start(`Phase 4/5 \u2014 Generating ${remainingPages.length} pages in parallel...`);
7845
+ const sharedLayoutNote = "Header and Footer are shared components rendered by the root layout. Do NOT include any site-wide <header>, <nav>, or <footer> in this page. Start with the main content directly.";
7846
+ const sharedComponentsNote = buildSharedComponentsNote(parseOpts.sharedComponentsSummary);
7703
7847
  const routeNote = `EXISTING ROUTES in this project: ${allRoutes}. All internal links MUST point to one of these routes. If a target doesn't exist, use href="#".`;
7704
7848
  const alignmentNote = 'CRITICAL LAYOUT RULE: Every <section> must wrap its content in a container div matching the header width. Use the EXACT same container classes as shown in the style context (e.g. className="container max-w-6xl px-4" or className="max-w-6xl mx-auto px-4"). Inner content can use narrower max-w for text centering, but the outer section container MUST match.';
7705
7849
  const existingPagesContext = buildExistingPagesContext(modCtx.config);
@@ -7708,25 +7852,29 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
7708
7852
  const remainingRequests = await pMap(
7709
7853
  remainingPages,
7710
7854
  async ({ name, id, route }) => {
7855
+ const isAuth = isAuthRoute(route) || isAuthRoute(name);
7856
+ const authNote = isAuth ? 'For this auth page: use centered card layout with outer div className="flex min-h-svh flex-col items-center justify-center p-6 md:p-10" and inner div className="w-full max-w-sm". Do NOT use section containers or full-width wrappers. The auth layout provides centering \u2014 just output the card content.' : void 0;
7711
7857
  const prompt = [
7712
7858
  `Create ONE page called "${name}" at route "${route}".`,
7713
7859
  `Context: ${message}.`,
7714
7860
  `Generate complete pageCode for this single page only. Do not generate other pages.`,
7715
- sharedNote,
7861
+ sharedLayoutNote,
7862
+ sharedComponentsNote,
7716
7863
  routeNote,
7717
7864
  alignmentNote,
7865
+ authNote,
7718
7866
  existingPagesContext,
7719
7867
  styleContext
7720
7868
  ].filter(Boolean).join("\n\n");
7721
7869
  try {
7722
7870
  const result = await parseModification(prompt, modCtx, provider, parseOpts);
7723
7871
  phase4Done++;
7724
- spinner.text = `Phase 4/4 \u2014 ${phase4Done}/${remainingPages.length} pages generated...`;
7872
+ spinner.text = `Phase 4/5 \u2014 ${phase4Done}/${remainingPages.length} pages generated...`;
7725
7873
  const codePage = result.requests.find((r) => r.type === "add-page");
7726
7874
  return codePage || { type: "add-page", target: "new", changes: { id, name, route } };
7727
7875
  } catch {
7728
7876
  phase4Done++;
7729
- spinner.text = `Phase 4/4 \u2014 ${phase4Done}/${remainingPages.length} pages generated...`;
7877
+ spinner.text = `Phase 4/5 \u2014 ${phase4Done}/${remainingPages.length} pages generated...`;
7730
7878
  return { type: "add-page", target: "new", changes: { id, name, route } };
7731
7879
  }
7732
7880
  },
@@ -7734,18 +7882,24 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
7734
7882
  );
7735
7883
  const allRequests = reusedExistingAnchor ? [...remainingRequests] : homeRequest ? [homeRequest, ...remainingRequests] : [...remainingRequests];
7736
7884
  const emptyPages = allRequests.filter((r) => r.type === "add-page" && !r.changes?.pageCode);
7737
- if (emptyPages.length > 0 && emptyPages.length <= 5) {
7885
+ if (emptyPages.length > 0) {
7738
7886
  spinner.text = `Retrying ${emptyPages.length} page(s) without code...`;
7739
7887
  for (const req of emptyPages) {
7740
7888
  const page = req.changes;
7741
7889
  const pageName = page.name || page.id || "page";
7742
7890
  const pageRoute = page.route || `/${pageName.toLowerCase()}`;
7743
7891
  try {
7892
+ const lightweightPrompt = buildLightweightPagePrompt(
7893
+ pageName,
7894
+ pageRoute,
7895
+ styleContext || "",
7896
+ parseOpts.sharedComponentsSummary
7897
+ );
7744
7898
  const retryResult = await parseModification(
7745
- `Create ONE page called "${pageName}" at route "${pageRoute}". Context: ${message}. Generate complete pageCode for this single page only.`,
7899
+ lightweightPrompt,
7746
7900
  modCtx,
7747
7901
  provider,
7748
- parseOpts
7902
+ { ...parseOpts, lightweight: true }
7749
7903
  );
7750
7904
  const codePage = retryResult.requests.find((r) => r.type === "add-page");
7751
7905
  if (codePage && codePage.changes?.pageCode) {
@@ -7757,9 +7911,77 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
7757
7911
  }
7758
7912
  }
7759
7913
  const withCode = allRequests.filter((r) => r.changes?.pageCode).length;
7760
- spinner.succeed(`Phase 4/4 \u2014 Generated ${allRequests.length} pages (${withCode} with full code)`);
7914
+ spinner.succeed(`Phase 4/5 \u2014 Generated ${allRequests.length} pages (${withCode} with full code)`);
7761
7915
  return allRequests;
7762
7916
  }
7917
+ var SharedExtractionItemSchema = z.object({
7918
+ name: z.string().min(2).max(50),
7919
+ type: z.enum(["section", "widget"]),
7920
+ description: z.string().max(200).default(""),
7921
+ propsInterface: z.string().default("{}"),
7922
+ code: z.string()
7923
+ });
7924
+ var SharedExtractionResponseSchema = z.object({
7925
+ components: z.array(SharedExtractionItemSchema).max(5).default([])
7926
+ });
7927
+ async function extractSharedComponents(homePageCode, projectRoot, aiProvider) {
7928
+ const manifest = await loadManifest5(projectRoot);
7929
+ let ai;
7930
+ try {
7931
+ ai = await createAIProvider(aiProvider);
7932
+ } catch {
7933
+ return { components: [], summary: buildSharedComponentsSummary(manifest) };
7934
+ }
7935
+ if (!ai.extractSharedComponents) {
7936
+ return { components: [], summary: buildSharedComponentsSummary(manifest) };
7937
+ }
7938
+ let rawItems;
7939
+ try {
7940
+ const reservedNames = getComponentProvider().listNames();
7941
+ const existingNames = manifest.shared.map((e) => e.name);
7942
+ const result = await ai.extractSharedComponents(homePageCode, reservedNames, existingNames);
7943
+ const parsed = SharedExtractionResponseSchema.safeParse(result);
7944
+ rawItems = parsed.success ? parsed.data.components : [];
7945
+ } catch {
7946
+ return { components: [], summary: buildSharedComponentsSummary(manifest) };
7947
+ }
7948
+ const reservedSet = new Set(
7949
+ getComponentProvider().listNames().map((n) => n.toLowerCase())
7950
+ );
7951
+ const existingSet = new Set(manifest.shared.map((e) => e.name.toLowerCase()));
7952
+ const seenNames = /* @__PURE__ */ new Set();
7953
+ const filtered = rawItems.filter((item) => {
7954
+ if (item.code.split("\n").length < 10) return false;
7955
+ if (reservedSet.has(item.name.toLowerCase())) return false;
7956
+ if (existingSet.has(item.name.toLowerCase())) return false;
7957
+ if (seenNames.has(item.name.toLowerCase())) return false;
7958
+ seenNames.add(item.name.toLowerCase());
7959
+ return true;
7960
+ });
7961
+ const results = [];
7962
+ const provider = getComponentProvider();
7963
+ for (const item of filtered) {
7964
+ try {
7965
+ const { code: fixedCode } = await autoFixCode(item.code);
7966
+ const shadcnImports = [...fixedCode.matchAll(/from\s+["']@\/components\/ui\/(.+?)["']/g)];
7967
+ for (const match of shadcnImports) {
7968
+ await provider.installComponent(match[1], projectRoot);
7969
+ }
7970
+ const result = await generateSharedComponent2(projectRoot, {
7971
+ name: item.name,
7972
+ type: item.type,
7973
+ code: fixedCode,
7974
+ description: item.description,
7975
+ propsInterface: item.propsInterface,
7976
+ usedIn: []
7977
+ });
7978
+ results.push(result);
7979
+ } catch {
7980
+ }
7981
+ }
7982
+ const updatedManifest = await loadManifest5(projectRoot);
7983
+ return { components: results, summary: buildSharedComponentsSummary(updatedManifest) };
7984
+ }
7763
7985
  function extractAppNameFromPrompt(prompt) {
7764
7986
  const patterns = [
7765
7987
  /(?:called|named|app\s+name)\s+["']([^"']+)["']/i,
@@ -7807,16 +8029,16 @@ import { dirname as dirname6 } from "path";
7807
8029
  import chalk11 from "chalk";
7808
8030
  import {
7809
8031
  getTemplateForPageType,
7810
- loadManifest as loadManifest5,
8032
+ loadManifest as loadManifest6,
7811
8033
  saveManifest,
7812
8034
  updateUsedIn,
7813
8035
  findSharedComponentByIdOrName,
7814
- generateSharedComponent as generateSharedComponent3
8036
+ generateSharedComponent as generateSharedComponent4
7815
8037
  } from "@getcoherent/core";
7816
8038
 
7817
8039
  // src/commands/chat/code-generator.ts
7818
8040
  import { resolve as resolve6 } from "path";
7819
- import { existsSync as existsSync14 } from "fs";
8041
+ import { existsSync as existsSync14, readdirSync as readdirSync2, readFileSync as readFileSync9 } from "fs";
7820
8042
  import { mkdir as mkdir3 } from "fs/promises";
7821
8043
  import { dirname as dirname5 } from "path";
7822
8044
  import {
@@ -7824,7 +8046,7 @@ import {
7824
8046
  PageGenerator,
7825
8047
  TailwindConfigGenerator
7826
8048
  } from "@getcoherent/core";
7827
- import { integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout2, generateSharedComponent as generateSharedComponent2 } from "@getcoherent/core";
8049
+ import { integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout2, generateSharedComponent as generateSharedComponent3 } from "@getcoherent/core";
7828
8050
  import chalk9 from "chalk";
7829
8051
 
7830
8052
  // src/utils/file-hashes.ts
@@ -7950,7 +8172,7 @@ async function regenerateLayout(config2, projectRoot, options = { navChanged: fa
7950
8172
  if (navType === "header" || navType === "both") {
7951
8173
  if (await canOverwriteShared(projectRoot, "components/shared/header.tsx", hashes)) {
7952
8174
  const headerCode = generator.generateSharedHeaderCode();
7953
- await generateSharedComponent2(projectRoot, {
8175
+ await generateSharedComponent3(projectRoot, {
7954
8176
  name: "Header",
7955
8177
  type: "layout",
7956
8178
  code: headerCode,
@@ -7962,7 +8184,7 @@ async function regenerateLayout(config2, projectRoot, options = { navChanged: fa
7962
8184
  }
7963
8185
  if (await canOverwriteShared(projectRoot, "components/shared/footer.tsx", hashes)) {
7964
8186
  const footerCode = generator.generateSharedFooterCode();
7965
- await generateSharedComponent2(projectRoot, {
8187
+ await generateSharedComponent3(projectRoot, {
7966
8188
  name: "Footer",
7967
8189
  type: "layout",
7968
8190
  code: footerCode,
@@ -7974,7 +8196,7 @@ async function regenerateLayout(config2, projectRoot, options = { navChanged: fa
7974
8196
  if (navType === "sidebar" || navType === "both") {
7975
8197
  if (await canOverwriteShared(projectRoot, "components/shared/sidebar.tsx", hashes)) {
7976
8198
  const sidebarCode = generator.generateSharedSidebarCode();
7977
- await generateSharedComponent2(projectRoot, {
8199
+ await generateSharedComponent3(projectRoot, {
7978
8200
  name: "AppSidebar",
7979
8201
  type: "layout",
7980
8202
  code: sidebarCode,
@@ -7996,6 +8218,28 @@ async function regenerateLayout(config2, projectRoot, options = { navChanged: fa
7996
8218
  }
7997
8219
  }
7998
8220
  }
8221
+ async function scanAndInstallSharedDeps(projectRoot) {
8222
+ const sharedDir = resolve6(projectRoot, "components", "shared");
8223
+ if (!existsSync14(sharedDir)) return [];
8224
+ const files = readdirSync2(sharedDir).filter((f) => f.endsWith(".tsx") || f.endsWith(".ts"));
8225
+ const installed = [];
8226
+ const provider = getComponentProvider();
8227
+ for (const file of files) {
8228
+ const code = readFileSync9(resolve6(sharedDir, file), "utf-8");
8229
+ const importMatches = [...code.matchAll(/@\/components\/ui\/([a-z0-9-]+)/g)];
8230
+ for (const [, componentId] of importMatches) {
8231
+ const uiPath = resolve6(projectRoot, "components", "ui", `${componentId}.tsx`);
8232
+ if (!existsSync14(uiPath) && provider.has(componentId)) {
8233
+ try {
8234
+ await provider.installComponent(componentId, projectRoot);
8235
+ installed.push(componentId);
8236
+ } catch {
8237
+ }
8238
+ }
8239
+ }
8240
+ }
8241
+ return [...new Set(installed)];
8242
+ }
7999
8243
  async function ensureAppRouteGroupLayout(projectRoot, navType, forceUpdate = false) {
8000
8244
  const layoutPath = resolve6(projectRoot, "app", "(app)", "layout.tsx");
8001
8245
  if (existsSync14(layoutPath) && !forceUpdate) return;
@@ -8053,6 +8297,10 @@ async function regenerateFiles(modified, config2, projectRoot, options = { navCh
8053
8297
  navChanged: options.navChanged,
8054
8298
  storedHashes: options.storedHashes
8055
8299
  });
8300
+ const sharedInstalled = await scanAndInstallSharedDeps(projectRoot);
8301
+ if (sharedInstalled.length > 0 && process.env.COHERENT_DEBUG === "1") {
8302
+ console.log(chalk9.dim(` Auto-installed shared deps: ${sharedInstalled.join(", ")}`));
8303
+ }
8056
8304
  }
8057
8305
  if (componentIds.size > 0) {
8058
8306
  const twGen = new TailwindConfigGenerator(config2);
@@ -8526,7 +8774,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
8526
8774
  fixes.forEach((f) => console.log(chalk11.dim(` ${f}`)));
8527
8775
  }
8528
8776
  await writeFile(pageFilePath, fixedCode);
8529
- const manifest = await loadManifest5(projectRoot);
8777
+ const manifest = await loadManifest6(projectRoot);
8530
8778
  const usedIn = manifest.shared.find((e) => e.id === resolved.id)?.usedIn ?? [];
8531
8779
  const routePath = route.replace(/^\//, "");
8532
8780
  const filePathRel = routePath ? `app/${routePath}/page.tsx` : "app/page.tsx";
@@ -8596,7 +8844,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
8596
8844
  };
8597
8845
  }
8598
8846
  const extractedCode = await ai.extractBlockAsComponent(sourceCode, blockHint, componentName);
8599
- const created = await generateSharedComponent3(projectRoot, {
8847
+ const created = await generateSharedComponent4(projectRoot, {
8600
8848
  name: componentName,
8601
8849
  type: "section",
8602
8850
  code: extractedCode,
@@ -8631,7 +8879,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
8631
8879
  await writeFile(fullPath, fixedCode);
8632
8880
  usedInFiles.push(relPath);
8633
8881
  }
8634
- const manifest = await loadManifest5(projectRoot);
8882
+ const manifest = await loadManifest6(projectRoot);
8635
8883
  const nextManifest = updateUsedIn(manifest, created.id, usedInFiles);
8636
8884
  await saveManifest(projectRoot, nextManifest);
8637
8885
  printPromoteAndLinkReport({
@@ -8798,7 +9046,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
8798
9046
  }
8799
9047
  const { code: layoutStripped, stripped } = stripInlineLayoutElements(codeToWrite);
8800
9048
  codeToWrite = layoutStripped;
8801
- if (!isMarketingRoute(route)) {
9049
+ if (!isMarketingRoute(route) && !isAuthRoute(route)) {
8802
9050
  const { code: normalized, fixed: wrapperFixed } = normalizePageWrapper(codeToWrite);
8803
9051
  if (wrapperFixed) {
8804
9052
  codeToWrite = normalized;
@@ -8820,7 +9068,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
8820
9068
  cm.updateConfig(cfg);
8821
9069
  pm.updateConfig(cfg);
8822
9070
  }
8823
- const manifestForAudit = await loadManifest5(projectRoot);
9071
+ const manifestForAudit = await loadManifest6(projectRoot);
8824
9072
  await warnInlineDuplicates(projectRoot, page.name || page.id || route.slice(1), codeToWrite, manifestForAudit);
8825
9073
  const relFilePath = routeToRelPath(route, isAuth);
8826
9074
  printPostGenerationReport({
@@ -8998,7 +9246,7 @@ ${pagesCtx}`
8998
9246
  }
8999
9247
  const { code: layoutStripped, stripped } = stripInlineLayoutElements(codeToWrite);
9000
9248
  codeToWrite = layoutStripped;
9001
- if (!isMarketingRoute(route)) {
9249
+ if (!isMarketingRoute(route) && !isAuthRoute(route)) {
9002
9250
  const { code: normalized, fixed: wrapperFixed } = normalizePageWrapper(codeToWrite);
9003
9251
  if (wrapperFixed) {
9004
9252
  codeToWrite = normalized;
@@ -9020,7 +9268,7 @@ ${pagesCtx}`
9020
9268
  cm.updateConfig(cfg);
9021
9269
  pm.updateConfig(cfg);
9022
9270
  }
9023
- const manifestForAudit = await loadManifest5(projectRoot);
9271
+ const manifestForAudit = await loadManifest6(projectRoot);
9024
9272
  await warnInlineDuplicates(
9025
9273
  projectRoot,
9026
9274
  pageDef.name || pageDef.id || route.slice(1),
@@ -9063,7 +9311,7 @@ ${pagesCtx}`
9063
9311
  fixes.forEach((f) => console.log(chalk11.dim(` ${f}`)));
9064
9312
  }
9065
9313
  const relFilePath = routeToRelPath(route, isAuth);
9066
- const manifest = await loadManifest5(projectRoot);
9314
+ const manifest = await loadManifest6(projectRoot);
9067
9315
  printPostGenerationReport({
9068
9316
  action: "updated",
9069
9317
  pageTitle: pageDef.name || pageDef.id || "Page",
@@ -9119,6 +9367,11 @@ function inferPageType(route, name) {
9119
9367
  if (/\bchangelog\b/.test(key)) return "changelog";
9120
9368
  if (/\babout\b/.test(key)) return "about";
9121
9369
  if (/\bsettings?\b/.test(key)) return "settings";
9370
+ if (/\bteam\b|\bmember\b/.test(key)) return "team";
9371
+ if (/\btasks?\b/.test(key) && /\[id\]|\bdetail\b/.test(key)) return "task-detail";
9372
+ if (/\btasks?\b/.test(key)) return "tasks";
9373
+ if (/\breset.?password\b/.test(key)) return "reset-password";
9374
+ if (/\bprofile\b|\baccount\b/.test(key)) return "profile";
9122
9375
  return null;
9123
9376
  }
9124
9377
  function getDefaultContent(pageType, pageName) {
@@ -9162,6 +9415,26 @@ function getDefaultContent(pageType, pageName) {
9162
9415
  settings: {
9163
9416
  title: "Settings",
9164
9417
  description: "Manage your account and preferences"
9418
+ },
9419
+ team: {
9420
+ title: "Our Team",
9421
+ description: "Meet the people behind the product"
9422
+ },
9423
+ tasks: {
9424
+ title: "Tasks",
9425
+ description: "Manage and track your tasks"
9426
+ },
9427
+ "task-detail": {
9428
+ title: "Task Detail",
9429
+ description: "View task details and activity"
9430
+ },
9431
+ "reset-password": {
9432
+ title: "Reset Password",
9433
+ description: "Enter your new password"
9434
+ },
9435
+ profile: {
9436
+ title: "Profile",
9437
+ description: "View and edit your profile"
9165
9438
  }
9166
9439
  };
9167
9440
  return defaults[pageType] || { title: pageName, description: "" };
@@ -9179,8 +9452,8 @@ function hasNavChanged(before, after) {
9179
9452
  // src/commands/chat/interactive.ts
9180
9453
  import chalk12 from "chalk";
9181
9454
  import { resolve as resolve8 } from "path";
9182
- import { existsSync as existsSync15, readFileSync as readFileSync9, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5 } from "fs";
9183
- import { DesignSystemManager as DesignSystemManager6, ComponentManager as ComponentManager3, loadManifest as loadManifest6 } from "@getcoherent/core";
9455
+ import { existsSync as existsSync15, readFileSync as readFileSync10, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5 } from "fs";
9456
+ import { DesignSystemManager as DesignSystemManager6, ComponentManager as ComponentManager4, loadManifest as loadManifest7 } from "@getcoherent/core";
9184
9457
  var DEBUG3 = process.env.COHERENT_DEBUG === "1";
9185
9458
  async function interactiveChat(options, chatCommandFn) {
9186
9459
  const { createInterface } = await import("readline");
@@ -9191,7 +9464,7 @@ async function interactiveChat(options, chatCommandFn) {
9191
9464
  const config2 = await loadConfig(configPath);
9192
9465
  const dsm = new DesignSystemManager6(configPath);
9193
9466
  await dsm.load();
9194
- const cm = new ComponentManager3(config2);
9467
+ const cm = new ComponentManager4(config2);
9195
9468
  const validProviders = ["claude", "openai", "auto"];
9196
9469
  const provider = (options.provider || "auto").toLowerCase();
9197
9470
  if (!validProviders.includes(provider)) {
@@ -9205,7 +9478,7 @@ async function interactiveChat(options, chatCommandFn) {
9205
9478
  try {
9206
9479
  mkdirSync5(historyDir, { recursive: true });
9207
9480
  if (existsSync15(historyFile)) {
9208
- history = readFileSync9(historyFile, "utf-8").split("\n").filter(Boolean).slice(-200);
9481
+ history = readFileSync10(historyFile, "utf-8").split("\n").filter(Boolean).slice(-200);
9209
9482
  }
9210
9483
  } catch (e) {
9211
9484
  if (DEBUG3) console.error("Failed to load REPL history:", e);
@@ -9250,7 +9523,7 @@ async function interactiveChat(options, chatCommandFn) {
9250
9523
  return;
9251
9524
  }
9252
9525
  if (lower === "components" || lower === "list components" || lower.includes("what components")) {
9253
- const manifest = await loadManifest6(projectRoot);
9526
+ const manifest = await loadManifest7(projectRoot);
9254
9527
  if (manifest.shared.length === 0) {
9255
9528
  console.log(chalk12.gray("\n No shared components yet.\n"));
9256
9529
  } else {
@@ -9284,7 +9557,7 @@ async function interactiveChat(options, chatCommandFn) {
9284
9557
  }
9285
9558
  if (lower === "status") {
9286
9559
  const currentConfig = dsm.getConfig();
9287
- const manifest = await loadManifest6(projectRoot);
9560
+ const manifest = await loadManifest7(projectRoot);
9288
9561
  console.log(chalk12.bold(`
9289
9562
  ${currentConfig.name || "Coherent Project"}`));
9290
9563
  console.log(
@@ -9417,7 +9690,7 @@ async function chatCommand(message, options) {
9417
9690
  const storedHashes = await loadHashes(projectRoot);
9418
9691
  const dsm = new DesignSystemManager7(configPath);
9419
9692
  await dsm.load();
9420
- const cm = new ComponentManager4(config2);
9693
+ const cm = new ComponentManager5(config2);
9421
9694
  const pm = new PageManager3(config2, cm);
9422
9695
  spinner.succeed("Configuration loaded");
9423
9696
  message = await resolveTargetFlags(message, options, config2, projectRoot);
@@ -9473,7 +9746,7 @@ async function chatCommand(message, options) {
9473
9746
  }
9474
9747
  }
9475
9748
  spinner.start("Parsing your request...");
9476
- let manifest = await loadManifest7(project.root);
9749
+ let manifest = await loadManifest8(project.root);
9477
9750
  const validShared = manifest.shared.filter((s) => {
9478
9751
  const fp = resolve9(project.root, s.file);
9479
9752
  return existsSync16(fp);
@@ -9486,12 +9759,7 @@ async function chatCommand(message, options) {
9486
9759
  console.log(chalk13.dim(`[pre-gen] Cleaned ${cleaned} orphaned component(s) from manifest`));
9487
9760
  }
9488
9761
  }
9489
- const sharedComponentsSummary = manifest.shared.length > 0 ? manifest.shared.map((e) => {
9490
- const importPath = e.file.replace(/^components\/shared\//, "").replace(/\.tsx$/, "");
9491
- const desc = e.description ? ` \u2014 ${e.description}` : "";
9492
- return ` ${e.id} ${e.name} (${e.type})${desc}
9493
- Import: @/components/shared/${importPath}`;
9494
- }).join("\n") : void 0;
9762
+ const sharedComponentsSummary = buildSharedComponentsSummary(manifest);
9495
9763
  if (DEBUG4 && sharedComponentsSummary) {
9496
9764
  console.log(chalk13.dim("[add-page] sharedComponentsSummary in prompt:\n" + sharedComponentsSummary));
9497
9765
  }
@@ -9677,7 +9945,7 @@ async function chatCommand(message, options) {
9677
9945
  try {
9678
9946
  const sharedPath = resolve9(projectRoot, entry.file);
9679
9947
  if (existsSync16(sharedPath)) {
9680
- const sharedCode = readFileSync10(sharedPath, "utf-8");
9948
+ const sharedCode = readFileSync11(sharedPath, "utf-8");
9681
9949
  const sharedImports = sharedCode.matchAll(/@\/components\/ui\/([a-z0-9-]+)/g);
9682
9950
  for (const m of sharedImports) {
9683
9951
  if (m[1]) allNeededComponentIds.add(m[1]);
@@ -9719,7 +9987,10 @@ async function chatCommand(message, options) {
9719
9987
  if (DEBUG4) console.log(chalk13.gray(` [DEBUG] installComponent result: ${result.success}`));
9720
9988
  if (result.success && result.componentDef) {
9721
9989
  if (!cm.read(componentId)) {
9722
- if (DEBUG4) console.log(chalk13.gray(` [DEBUG] Registering ${result.componentDef.id} (${result.componentDef.name})`));
9990
+ if (DEBUG4)
9991
+ console.log(
9992
+ chalk13.gray(` [DEBUG] Registering ${result.componentDef.id} (${result.componentDef.name})`)
9993
+ );
9723
9994
  const regResult = await cm.register(result.componentDef);
9724
9995
  if (DEBUG4) {
9725
9996
  console.log(
@@ -9799,6 +10070,17 @@ async function chatCommand(message, options) {
9799
10070
  const result = await applyModification(request, dsm, cm, pm, projectRoot, provider, message);
9800
10071
  results.push(result);
9801
10072
  }
10073
+ for (const request of normalizedRequests) {
10074
+ const changes = request.changes;
10075
+ if ((request.type === "add-page" || request.type === "update-page") && changes?.route === "/" && changes?.pageCode) {
10076
+ const cfg = dsm.getConfig();
10077
+ if (cfg.settings.homePagePlaceholder) {
10078
+ cfg.settings.homePagePlaceholder = false;
10079
+ dsm.updateConfig(cfg);
10080
+ }
10081
+ break;
10082
+ }
10083
+ }
9802
10084
  const currentConfig = dsm.getConfig();
9803
10085
  const autoScaffoldEnabled = currentConfig.settings.autoScaffold === true;
9804
10086
  const scaffoldedPages = [];
@@ -9812,7 +10094,7 @@ async function chatCommand(message, options) {
9812
10094
  let pageCode = "";
9813
10095
  if (existsSync16(pageFilePath)) {
9814
10096
  try {
9815
- pageCode = readFileSync10(pageFilePath, "utf-8");
10097
+ pageCode = readFileSync11(pageFilePath, "utf-8");
9816
10098
  } catch {
9817
10099
  }
9818
10100
  }
@@ -9923,7 +10205,7 @@ async function chatCommand(message, options) {
9923
10205
  for (const mod of result.modified) {
9924
10206
  if (mod.startsWith("app/") && mod.endsWith("/page.tsx")) {
9925
10207
  try {
9926
- const code = readFileSync10(resolve9(projectRoot, mod), "utf-8");
10208
+ const code = readFileSync11(resolve9(projectRoot, mod), "utf-8");
9927
10209
  const issues = validatePageQuality(code, allRoutes).filter(
9928
10210
  (i) => i.type === "BROKEN_INTERNAL_LINK"
9929
10211
  );
@@ -9993,6 +10275,10 @@ async function chatCommand(message, options) {
9993
10275
  await regenerateFiles(Array.from(allModified), updatedConfig, projectRoot, { navChanged, storedHashes });
9994
10276
  spinner.succeed("Files regenerated");
9995
10277
  }
10278
+ const finalDeps = await scanAndInstallSharedDeps(projectRoot);
10279
+ if (finalDeps.length > 0) {
10280
+ console.log(chalk13.dim(` Auto-installed shared deps: ${finalDeps.join(", ")}`));
10281
+ }
9996
10282
  try {
9997
10283
  fixGlobalsCss(projectRoot, updatedConfig);
9998
10284
  } catch {
@@ -10003,7 +10289,7 @@ async function chatCommand(message, options) {
10003
10289
  const layoutFile = resolve9(projectRoot, "app", "layout.tsx");
10004
10290
  const filesToHash = [layoutFile];
10005
10291
  if (existsSync16(sharedDir)) {
10006
- for (const f of readdirSync2(sharedDir)) {
10292
+ for (const f of readdirSync3(sharedDir)) {
10007
10293
  if (f.endsWith(".tsx")) filesToHash.push(resolve9(sharedDir, f));
10008
10294
  }
10009
10295
  }
@@ -10123,18 +10409,18 @@ ${uxRecommendations}
10123
10409
  import chalk14 from "chalk";
10124
10410
  import ora3 from "ora";
10125
10411
  import { spawn } from "child_process";
10126
- import { existsSync as existsSync19, rmSync as rmSync3, readFileSync as readFileSync13, writeFileSync as writeFileSync10 } from "fs";
10412
+ import { existsSync as existsSync19, rmSync as rmSync3, readFileSync as readFileSync14, writeFileSync as writeFileSync10, readdirSync as readdirSync5 } from "fs";
10127
10413
  import { resolve as resolve10, join as join14 } from "path";
10128
10414
  import { readdir as readdir2 } from "fs/promises";
10129
10415
  import { DesignSystemManager as DesignSystemManager8, ComponentGenerator as ComponentGenerator3 } from "@getcoherent/core";
10130
10416
 
10131
10417
  // src/utils/file-watcher.ts
10132
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, existsSync as existsSync18 } from "fs";
10418
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, existsSync as existsSync18 } from "fs";
10133
10419
  import { relative as relative4, join as join13 } from "path";
10134
- import { loadManifest as loadManifest8, saveManifest as saveManifest3 } from "@getcoherent/core";
10420
+ import { loadManifest as loadManifest9, saveManifest as saveManifest3 } from "@getcoherent/core";
10135
10421
 
10136
10422
  // src/utils/component-integrity.ts
10137
- import { existsSync as existsSync17, readFileSync as readFileSync11, readdirSync as readdirSync3 } from "fs";
10423
+ import { existsSync as existsSync17, readFileSync as readFileSync12, readdirSync as readdirSync4 } from "fs";
10138
10424
  import { join as join12, relative as relative3 } from "path";
10139
10425
  function extractExportedComponentNames(code) {
10140
10426
  const names = [];
@@ -10168,7 +10454,7 @@ function findPagesImporting(projectRoot, componentName, componentFile) {
10168
10454
  for (const absPath of pageFiles) {
10169
10455
  if (absPath.includes("design-system")) continue;
10170
10456
  try {
10171
- const code = readFileSync11(absPath, "utf-8");
10457
+ const code = readFileSync12(absPath, "utf-8");
10172
10458
  const hasNamedImport = new RegExp(`import\\s+\\{[^}]*\\b${componentName}\\b[^}]*\\}\\s+from\\s+['"]`).test(code);
10173
10459
  const hasDefaultImport = new RegExp(`import\\s+${componentName}\\s+from\\s+['"]`).test(code);
10174
10460
  const hasPathImport = code.includes(`@/${componentImportPath}`);
@@ -10184,7 +10470,7 @@ function isUsedInLayout(projectRoot, componentName) {
10184
10470
  const layoutPath = join12(projectRoot, "app", "layout.tsx");
10185
10471
  if (!existsSync17(layoutPath)) return false;
10186
10472
  try {
10187
- const code = readFileSync11(layoutPath, "utf-8");
10473
+ const code = readFileSync12(layoutPath, "utf-8");
10188
10474
  return code.includes(componentName);
10189
10475
  } catch {
10190
10476
  return false;
@@ -10205,7 +10491,7 @@ function findUnregisteredComponents(projectRoot, manifest) {
10205
10491
  const relFile = relative3(projectRoot, absPath);
10206
10492
  if (registeredFiles.has(relFile)) continue;
10207
10493
  try {
10208
- const code = readFileSync11(absPath, "utf-8");
10494
+ const code = readFileSync12(absPath, "utf-8");
10209
10495
  const exports = extractExportedComponentNames(code);
10210
10496
  for (const name of exports) {
10211
10497
  if (registeredNames.has(name)) continue;
@@ -10227,7 +10513,7 @@ function findInlineDuplicates(projectRoot, manifest) {
10227
10513
  if (absPath.includes("design-system")) continue;
10228
10514
  let code;
10229
10515
  try {
10230
- code = readFileSync11(absPath, "utf-8");
10516
+ code = readFileSync12(absPath, "utf-8");
10231
10517
  } catch {
10232
10518
  continue;
10233
10519
  }
@@ -10259,7 +10545,7 @@ function findComponentFileByExportName(projectRoot, componentName) {
10259
10545
  );
10260
10546
  for (const absPath of files) {
10261
10547
  try {
10262
- const code = readFileSync11(absPath, "utf-8");
10548
+ const code = readFileSync12(absPath, "utf-8");
10263
10549
  const exports = extractExportedComponentNames(code);
10264
10550
  if (exports.includes(componentName)) {
10265
10551
  return relative3(projectRoot, absPath);
@@ -10304,7 +10590,7 @@ function reconcileComponents(projectRoot, manifest) {
10304
10590
  }
10305
10591
  let code;
10306
10592
  try {
10307
- code = readFileSync11(join12(projectRoot, entry.file), "utf-8");
10593
+ code = readFileSync12(join12(projectRoot, entry.file), "utf-8");
10308
10594
  } catch {
10309
10595
  return true;
10310
10596
  }
@@ -10381,7 +10667,7 @@ function collectFiles(dir, filter, skipDirs = []) {
10381
10667
  function walk(d) {
10382
10668
  let entries;
10383
10669
  try {
10384
- entries = readdirSync3(d, { withFileTypes: true });
10670
+ entries = readdirSync4(d, { withFileTypes: true });
10385
10671
  } catch {
10386
10672
  return;
10387
10673
  }
@@ -10427,7 +10713,7 @@ function getWatcherConfig(projectRoot) {
10427
10713
  try {
10428
10714
  const pkgPath = join13(projectRoot, "package.json");
10429
10715
  if (!existsSync18(pkgPath)) return defaultWatcherConfig();
10430
- const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
10716
+ const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
10431
10717
  const c = pkg?.coherent?.watcher ?? {};
10432
10718
  return {
10433
10719
  enabled: c.enabled !== false,
@@ -10455,7 +10741,7 @@ async function handleFileChange(projectRoot, filePath) {
10455
10741
  if (relativePath.includes("node_modules") || relativePath.includes(".next")) return;
10456
10742
  let content;
10457
10743
  try {
10458
- content = readFileSync12(filePath, "utf-8");
10744
+ content = readFileSync13(filePath, "utf-8");
10459
10745
  } catch {
10460
10746
  return;
10461
10747
  }
@@ -10485,7 +10771,7 @@ async function handleFileChange(projectRoot, filePath) {
10485
10771
  if (config2.warnSharedReuse) {
10486
10772
  let manifest;
10487
10773
  try {
10488
- manifest = await loadManifest8(projectRoot);
10774
+ manifest = await loadManifest9(projectRoot);
10489
10775
  } catch {
10490
10776
  manifest = { shared: [], nextId: 1 };
10491
10777
  }
@@ -10504,7 +10790,7 @@ async function handleFileDelete(projectRoot, filePath) {
10504
10790
  if (!relativePath.startsWith("components/") || relativePath.startsWith("components/ui/")) return;
10505
10791
  try {
10506
10792
  const chalk33 = (await import("chalk")).default;
10507
- const manifest = await loadManifest8(projectRoot);
10793
+ const manifest = await loadManifest9(projectRoot);
10508
10794
  const orphaned = manifest.shared.find((s) => s.file === relativePath);
10509
10795
  if (orphaned) {
10510
10796
  const cleaned = {
@@ -10525,10 +10811,10 @@ async function detectNewComponent(projectRoot, filePath) {
10525
10811
  if (!relativePath.endsWith(".tsx") && !relativePath.endsWith(".jsx")) return;
10526
10812
  try {
10527
10813
  const chalk33 = (await import("chalk")).default;
10528
- const manifest = await loadManifest8(projectRoot);
10814
+ const manifest = await loadManifest9(projectRoot);
10529
10815
  const alreadyRegistered = manifest.shared.some((s) => s.file === relativePath);
10530
10816
  if (alreadyRegistered) return;
10531
- const code = readFileSync12(filePath, "utf-8");
10817
+ const code = readFileSync13(filePath, "utf-8");
10532
10818
  const exports = extractExportedComponentNames(code);
10533
10819
  if (exports.length > 0) {
10534
10820
  const alreadyByName = exports.every((n) => manifest.shared.some((s) => s.name === n));
@@ -10652,7 +10938,7 @@ async function validateSyntax(projectRoot) {
10652
10938
  const appDir = join14(projectRoot, "app");
10653
10939
  const pages = await listPageFiles(appDir);
10654
10940
  for (const file of pages) {
10655
- const content = readFileSync13(file, "utf-8");
10941
+ const content = readFileSync14(file, "utf-8");
10656
10942
  const fixed = fixUnescapedLtInJsx(sanitizeMetadataStrings(ensureUseClientIfNeeded(content)));
10657
10943
  if (fixed !== content) {
10658
10944
  writeFileSync10(file, fixed, "utf-8");
@@ -10665,9 +10951,14 @@ async function fixMissingComponentExports(projectRoot) {
10665
10951
  const uiDir = join14(projectRoot, "components", "ui");
10666
10952
  if (!existsSync19(appDir) || !existsSync19(uiDir)) return;
10667
10953
  const pages = await listPageFiles(appDir);
10954
+ const sharedDir = join14(projectRoot, "components", "shared");
10955
+ if (existsSync19(sharedDir)) {
10956
+ const sharedFiles = readdirSync5(sharedDir).filter((f) => f.endsWith(".tsx") || f.endsWith(".ts")).map((f) => join14(sharedDir, f));
10957
+ pages.push(...sharedFiles);
10958
+ }
10668
10959
  const neededExports = /* @__PURE__ */ new Map();
10669
10960
  for (const file of pages) {
10670
- const content = readFileSync13(file, "utf-8");
10961
+ const content = readFileSync14(file, "utf-8");
10671
10962
  const importRe = /import\s*\{([^}]+)\}\s*from\s*['"]@\/components\/ui\/([^'"]+)['"]/g;
10672
10963
  let m;
10673
10964
  while ((m = importRe.exec(content)) !== null) {
@@ -10711,7 +11002,7 @@ async function fixMissingComponentExports(projectRoot) {
10711
11002
  }
10712
11003
  continue;
10713
11004
  }
10714
- const content = readFileSync13(componentFile, "utf-8");
11005
+ const content = readFileSync14(componentFile, "utf-8");
10715
11006
  const exportRe = /export\s+(?:const|function|class)\s+(\w+)|export\s*\{([^}]+)\}/g;
10716
11007
  const existingExports = /* @__PURE__ */ new Set();
10717
11008
  let em;
@@ -10764,7 +11055,7 @@ async function backfillPageAnalysis(projectRoot) {
10764
11055
  filePath = join14(projectRoot, "app", route.slice(1), "page.tsx");
10765
11056
  }
10766
11057
  if (!existsSync19(filePath)) continue;
10767
- const code = readFileSync13(filePath, "utf-8");
11058
+ const code = readFileSync14(filePath, "utf-8");
10768
11059
  if (code.length < 50) continue;
10769
11060
  page.pageAnalysis = analyzePageCode(code);
10770
11061
  changed = true;
@@ -11005,7 +11296,7 @@ async function previewCommand() {
11005
11296
  import chalk15 from "chalk";
11006
11297
  import ora4 from "ora";
11007
11298
  import { spawn as spawn2 } from "child_process";
11008
- import { existsSync as existsSync20, rmSync as rmSync4, readdirSync as readdirSync4 } from "fs";
11299
+ import { existsSync as existsSync20, rmSync as rmSync4, readdirSync as readdirSync6 } from "fs";
11009
11300
  import { resolve as resolve11, join as join15, dirname as dirname7 } from "path";
11010
11301
  import { readdir as readdir3, readFile as readFile6, writeFile as writeFile5, mkdir as mkdir5, copyFile as copyFile2 } from "fs/promises";
11011
11302
  var COPY_EXCLUDE = /* @__PURE__ */ new Set([
@@ -11126,7 +11417,7 @@ function countComponents(outRoot) {
11126
11417
  const dir = join15(outRoot, "components", sub);
11127
11418
  if (!existsSync20(dir)) continue;
11128
11419
  try {
11129
- n += readdirSync4(dir).filter((f) => f.endsWith(".tsx") || f.endsWith(".jsx")).length;
11420
+ n += readdirSync6(dir).filter((f) => f.endsWith(".tsx") || f.endsWith(".jsx")).length;
11130
11421
  } catch {
11131
11422
  }
11132
11423
  }
@@ -11445,14 +11736,14 @@ async function regenerateDocsCommand() {
11445
11736
 
11446
11737
  // src/commands/fix.ts
11447
11738
  import chalk18 from "chalk";
11448
- import { readdirSync as readdirSync5, readFileSync as readFileSync14, existsSync as existsSync21, writeFileSync as writeFileSync11, rmSync as rmSync5, mkdirSync as mkdirSync7 } from "fs";
11739
+ import { readdirSync as readdirSync7, readFileSync as readFileSync15, existsSync as existsSync21, writeFileSync as writeFileSync11, rmSync as rmSync5, mkdirSync as mkdirSync7 } from "fs";
11449
11740
  import { resolve as resolve12, join as join16 } from "path";
11450
11741
  import {
11451
11742
  DesignSystemManager as DesignSystemManager11,
11452
- ComponentManager as ComponentManager5,
11743
+ ComponentManager as ComponentManager6,
11453
11744
  PageManager as PageManager4,
11454
11745
  ComponentGenerator as ComponentGenerator4,
11455
- loadManifest as loadManifest9,
11746
+ loadManifest as loadManifest10,
11456
11747
  saveManifest as saveManifest4
11457
11748
  } from "@getcoherent/core";
11458
11749
  function extractComponentIdsFromCode2(code) {
@@ -11470,7 +11761,7 @@ function extractComponentIdsFromCode2(code) {
11470
11761
  function listTsxFiles(dir) {
11471
11762
  const files = [];
11472
11763
  try {
11473
- const entries = readdirSync5(dir, { withFileTypes: true });
11764
+ const entries = readdirSync7(dir, { withFileTypes: true });
11474
11765
  for (const e of entries) {
11475
11766
  const full = join16(dir, e.name);
11476
11767
  if (e.isDirectory() && e.name !== "node_modules" && !e.name.startsWith(".")) {
@@ -11529,7 +11820,7 @@ async function fixCommand(opts = {}) {
11529
11820
  const componentsTsxFiles = listTsxFiles(resolve12(projectRoot, "components"));
11530
11821
  const allComponentIds = /* @__PURE__ */ new Set();
11531
11822
  for (const file of [...allTsxFiles, ...componentsTsxFiles]) {
11532
- const content = readFileSync14(file, "utf-8");
11823
+ const content = readFileSync15(file, "utf-8");
11533
11824
  extractComponentIdsFromCode2(content).forEach((id) => allComponentIds.add(id));
11534
11825
  }
11535
11826
  let dsm = null;
@@ -11539,7 +11830,7 @@ async function fixCommand(opts = {}) {
11539
11830
  dsm = new DesignSystemManager11(project.configPath);
11540
11831
  await dsm.load();
11541
11832
  const config2 = dsm.getConfig();
11542
- cm = new ComponentManager5(config2);
11833
+ cm = new ComponentManager6(config2);
11543
11834
  pm = new PageManager4(config2, cm);
11544
11835
  const missingComponents = [];
11545
11836
  const missingFiles = [];
@@ -11601,7 +11892,7 @@ async function fixCommand(opts = {}) {
11601
11892
  const userTsxFiles = allTsxFiles.filter((f) => !f.includes("/design-system/"));
11602
11893
  let syntaxFixed = 0;
11603
11894
  for (const file of userTsxFiles) {
11604
- const content = readFileSync14(file, "utf-8");
11895
+ const content = readFileSync15(file, "utf-8");
11605
11896
  const fixed = fixUnescapedLtInJsx(
11606
11897
  fixEscapedClosingQuotes(sanitizeMetadataStrings(ensureUseClientIfNeeded(content)))
11607
11898
  );
@@ -11619,7 +11910,7 @@ async function fixCommand(opts = {}) {
11619
11910
  let qualityFixCount = 0;
11620
11911
  const qualityFixDetails = [];
11621
11912
  for (const file of userTsxFiles) {
11622
- const content = readFileSync14(file, "utf-8");
11913
+ const content = readFileSync15(file, "utf-8");
11623
11914
  const { code: autoFixed, fixes: fileFixes } = await autoFixCode(content);
11624
11915
  if (autoFixed !== content) {
11625
11916
  if (!dryRun) writeFileSync11(file, autoFixed, "utf-8");
@@ -11638,7 +11929,7 @@ async function fixCommand(opts = {}) {
11638
11929
  let totalWarnings = 0;
11639
11930
  const fileIssues = [];
11640
11931
  for (const file of allTsxFiles) {
11641
- const code = dryRun ? readFileSync14(file, "utf-8") : readFileSync14(file, "utf-8");
11932
+ const code = dryRun ? readFileSync15(file, "utf-8") : readFileSync15(file, "utf-8");
11642
11933
  const relativePath = file.replace(projectRoot + "/", "");
11643
11934
  const baseName = file.split("/").pop() || "";
11644
11935
  const isAuthPage = relativePath.includes("(auth)");
@@ -11662,7 +11953,7 @@ async function fixCommand(opts = {}) {
11662
11953
  fileIssues.push({ path: relativePath, report });
11663
11954
  }
11664
11955
  try {
11665
- let manifest = await loadManifest9(project.root);
11956
+ let manifest = await loadManifest10(project.root);
11666
11957
  let manifestModified = false;
11667
11958
  const { manifest: cleaned, removed: orphaned } = removeOrphanedEntries(project.root, manifest);
11668
11959
  if (orphaned.length > 0) {
@@ -11761,13 +12052,13 @@ async function fixCommand(opts = {}) {
11761
12052
  // src/commands/check.ts
11762
12053
  import chalk19 from "chalk";
11763
12054
  import { resolve as resolve13 } from "path";
11764
- import { readdirSync as readdirSync6, readFileSync as readFileSync15, statSync as statSync2, existsSync as existsSync22 } from "fs";
11765
- import { loadManifest as loadManifest10 } from "@getcoherent/core";
12055
+ import { readdirSync as readdirSync8, readFileSync as readFileSync16, statSync as statSync2, existsSync as existsSync22 } from "fs";
12056
+ import { loadManifest as loadManifest11 } from "@getcoherent/core";
11766
12057
  var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["node_modules", "design-system"]);
11767
12058
  function findTsxFiles(dir) {
11768
12059
  const results = [];
11769
12060
  try {
11770
- const entries = readdirSync6(dir);
12061
+ const entries = readdirSync8(dir);
11771
12062
  for (const entry of entries) {
11772
12063
  const full = resolve13(dir, entry);
11773
12064
  const stat = statSync2(full);
@@ -11818,7 +12109,7 @@ async function checkCommand(opts = {}) {
11818
12109
  "NATIVE_TABLE"
11819
12110
  ]);
11820
12111
  for (const file of files) {
11821
- const code = readFileSync15(file, "utf-8");
12112
+ const code = readFileSync16(file, "utf-8");
11822
12113
  const relativePath = file.replace(projectRoot + "/", "");
11823
12114
  const baseName = file.split("/").pop() || "";
11824
12115
  const isAuthPage = relativePath.includes("(auth)");
@@ -11860,7 +12151,7 @@ async function checkCommand(opts = {}) {
11860
12151
  routeSet.add("/");
11861
12152
  routeSet.add("#");
11862
12153
  for (const file of files) {
11863
- const code = readFileSync15(file, "utf-8");
12154
+ const code = readFileSync16(file, "utf-8");
11864
12155
  const relativePath = file.replace(projectRoot + "/", "");
11865
12156
  const lines = code.split("\n");
11866
12157
  const linkHrefRe = /href\s*=\s*["'](\/[a-z0-9/-]*)["']/gi;
@@ -11889,7 +12180,7 @@ async function checkCommand(opts = {}) {
11889
12180
  \u{1F517} Internal Links`) + chalk19.dim(` \u2014 all ${result.links.total} links resolve \u2713`));
11890
12181
  }
11891
12182
  try {
11892
- const manifest = await loadManifest10(project.root);
12183
+ const manifest = await loadManifest11(project.root);
11893
12184
  if (manifest.shared.length > 0) {
11894
12185
  for (const entry of manifest.shared) {
11895
12186
  const fullPath = resolve13(project.root, entry.file);
@@ -11905,7 +12196,7 @@ async function checkCommand(opts = {}) {
11905
12196
  }
11906
12197
  if (!skipShared) {
11907
12198
  try {
11908
- const manifest = await loadManifest10(projectRoot);
12199
+ const manifest = await loadManifest11(projectRoot);
11909
12200
  if (!opts.json && manifest.shared.length > 0) {
11910
12201
  console.log(chalk19.cyan(`
11911
12202
  \u{1F9E9} Shared Components`) + chalk19.dim(` (${manifest.shared.length} registered)
@@ -11928,7 +12219,7 @@ async function checkCommand(opts = {}) {
11928
12219
  continue;
11929
12220
  }
11930
12221
  try {
11931
- const code = readFileSync15(filePath, "utf-8");
12222
+ const code = readFileSync16(filePath, "utf-8");
11932
12223
  const actualExports = extractExportedComponentNames(code);
11933
12224
  if (actualExports.length > 0 && !actualExports.includes(entry.name)) {
11934
12225
  _nameMismatch++;
@@ -12086,9 +12377,9 @@ import { Command } from "commander";
12086
12377
  import chalk25 from "chalk";
12087
12378
  import {
12088
12379
  DesignSystemManager as DesignSystemManager12,
12089
- ComponentManager as ComponentManager6,
12090
- loadManifest as loadManifest11,
12091
- generateSharedComponent as generateSharedComponent4,
12380
+ ComponentManager as ComponentManager7,
12381
+ loadManifest as loadManifest12,
12382
+ generateSharedComponent as generateSharedComponent5,
12092
12383
  integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout3
12093
12384
  } from "@getcoherent/core";
12094
12385
  import { existsSync as existsSync23 } from "fs";
@@ -12127,8 +12418,8 @@ function createComponentsCommand() {
12127
12418
  const dsm = new DesignSystemManager12(project.configPath);
12128
12419
  await dsm.load();
12129
12420
  const config2 = dsm.getConfig();
12130
- const cm = new ComponentManager6(config2);
12131
- const manifest = await loadManifest11(project.root);
12421
+ const cm = new ComponentManager7(config2);
12422
+ const manifest = await loadManifest12(project.root);
12132
12423
  if (opts.json) {
12133
12424
  const installed2 = cm.getAllComponents();
12134
12425
  console.log(JSON.stringify({ shared: manifest.shared, ui: installed2 }, null, 2));
@@ -12180,7 +12471,7 @@ function createComponentsCommand() {
12180
12471
  sharedCmd.option("--json", "Machine-readable JSON output").option("--verbose", "Show file paths and usage details").action(async (opts) => {
12181
12472
  const project = findConfig();
12182
12473
  if (!project) exitNotCoherent();
12183
- const manifest = await loadManifest11(project.root);
12474
+ const manifest = await loadManifest12(project.root);
12184
12475
  if (opts.json) {
12185
12476
  console.log(JSON.stringify(manifest, null, 2));
12186
12477
  return;
@@ -12209,7 +12500,7 @@ function createComponentsCommand() {
12209
12500
  const project = findConfig();
12210
12501
  if (!project) exitNotCoherent();
12211
12502
  const type = opts.type === "section" || opts.type === "widget" ? opts.type : "layout";
12212
- const result = await generateSharedComponent4(project.root, {
12503
+ const result = await generateSharedComponent5(project.root, {
12213
12504
  name: name.trim(),
12214
12505
  type,
12215
12506
  description: opts.description,
@@ -12260,7 +12551,7 @@ import {
12260
12551
  EXAMPLE_MULTIPAGE_CONFIG,
12261
12552
  normalizeFigmaComponents,
12262
12553
  setSharedMapping,
12263
- generateSharedComponent as generateSharedComponent5,
12554
+ generateSharedComponent as generateSharedComponent6,
12264
12555
  generatePagesFromFigma,
12265
12556
  integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout4,
12266
12557
  DesignSystemManager as DesignSystemManager13,
@@ -12457,7 +12748,7 @@ async function importFigmaAction(urlOrKey, opts) {
12457
12748
  else {
12458
12749
  stats.sharedCount++;
12459
12750
  if (!dryRun) {
12460
- const { id, name, file } = await generateSharedComponent5(projectRoot, {
12751
+ const { id, name, file } = await generateSharedComponent6(projectRoot, {
12461
12752
  name: entry.suggestedName,
12462
12753
  type: "widget",
12463
12754
  code: entry.suggestedTsx,
@@ -12619,7 +12910,7 @@ async function dsRegenerateCommand() {
12619
12910
  // src/commands/update.ts
12620
12911
  import chalk28 from "chalk";
12621
12912
  import ora8 from "ora";
12622
- import { readFileSync as readFileSync16, existsSync as existsSync25 } from "fs";
12913
+ import { readFileSync as readFileSync17, existsSync as existsSync25 } from "fs";
12623
12914
  import { join as join19 } from "path";
12624
12915
  import { DesignSystemManager as DesignSystemManager15, CLI_VERSION as CLI_VERSION4 } from "@getcoherent/core";
12625
12916
 
@@ -12792,7 +13083,7 @@ function checkMissingCssVars(projectRoot) {
12792
13083
  const globalsPath = join19(projectRoot, "app", "globals.css");
12793
13084
  if (!existsSync25(globalsPath)) return [];
12794
13085
  try {
12795
- const content = readFileSync16(globalsPath, "utf-8");
13086
+ const content = readFileSync17(globalsPath, "utf-8");
12796
13087
  return EXPECTED_CSS_VARS.filter((v) => !content.includes(v));
12797
13088
  } catch {
12798
13089
  return [];
@@ -12802,7 +13093,7 @@ function patchGlobalsCss(projectRoot, missingVars) {
12802
13093
  const globalsPath = join19(projectRoot, "app", "globals.css");
12803
13094
  if (!existsSync25(globalsPath) || missingVars.length === 0) return;
12804
13095
  const { writeFileSync: writeFileSync14 } = __require("fs");
12805
- let content = readFileSync16(globalsPath, "utf-8");
13096
+ let content = readFileSync17(globalsPath, "utf-8");
12806
13097
  const defaultValues = {
12807
13098
  "--chart-1": "220 70% 50%",
12808
13099
  "--chart-2": "160 60% 45%",
@@ -12880,17 +13171,17 @@ async function undoCommand(options) {
12880
13171
  // src/commands/sync.ts
12881
13172
  import chalk30 from "chalk";
12882
13173
  import ora9 from "ora";
12883
- import { existsSync as existsSync26, readFileSync as readFileSync17 } from "fs";
13174
+ import { existsSync as existsSync26, readFileSync as readFileSync18 } from "fs";
12884
13175
  import { join as join20, relative as relative5, dirname as dirname10 } from "path";
12885
13176
  import { readdir as readdir4, readFile as readFile7 } from "fs/promises";
12886
13177
  import { DesignSystemManager as DesignSystemManager16 } from "@getcoherent/core";
12887
- import { loadManifest as loadManifest12, saveManifest as saveManifest5, findSharedComponent } from "@getcoherent/core";
13178
+ import { loadManifest as loadManifest13, saveManifest as saveManifest5, findSharedComponent } from "@getcoherent/core";
12888
13179
  function extractTokensFromProject(projectRoot) {
12889
13180
  const lightColors = {};
12890
13181
  const darkColors = {};
12891
13182
  const globalsPath = join20(projectRoot, "app", "globals.css");
12892
13183
  if (existsSync26(globalsPath)) {
12893
- const css = readFileSync17(globalsPath, "utf-8");
13184
+ const css = readFileSync18(globalsPath, "utf-8");
12894
13185
  const rootMatch = css.match(/:root\s*\{([^}]+)\}/s);
12895
13186
  if (rootMatch) parseVarsInto(rootMatch[1], lightColors);
12896
13187
  const darkMatch = css.match(/\.dark\s*\{([^}]+)\}/s);
@@ -12899,7 +13190,7 @@ function extractTokensFromProject(projectRoot) {
12899
13190
  const layoutPath = join20(projectRoot, "app", "layout.tsx");
12900
13191
  let layoutCode = "";
12901
13192
  if (existsSync26(layoutPath)) {
12902
- layoutCode = readFileSync17(layoutPath, "utf-8");
13193
+ layoutCode = readFileSync18(layoutPath, "utf-8");
12903
13194
  const rootInline = layoutCode.match(/:root\s*\{([^}]+)\}/s);
12904
13195
  if (rootInline && Object.keys(lightColors).length === 0) {
12905
13196
  parseVarsInto(rootInline[1], lightColors);
@@ -12917,7 +13208,7 @@ function extractTokensFromProject(projectRoot) {
12917
13208
  defaultMode = "dark";
12918
13209
  }
12919
13210
  let radius;
12920
- const allCss = [existsSync26(globalsPath) ? readFileSync17(globalsPath, "utf-8") : "", layoutCode].join("\n");
13211
+ const allCss = [existsSync26(globalsPath) ? readFileSync18(globalsPath, "utf-8") : "", layoutCode].join("\n");
12921
13212
  const radiusMatch = allCss.match(/--radius:\s*([^;]+);/);
12922
13213
  if (radiusMatch) radius = radiusMatch[1].trim();
12923
13214
  return {
@@ -13164,7 +13455,7 @@ async function syncCommand(options = {}) {
13164
13455
  let reconcileResult = null;
13165
13456
  if (doComponents) {
13166
13457
  spinner.start("Reconciling shared components...");
13167
- const manifest = await loadManifest12(project.root);
13458
+ const manifest = await loadManifest13(project.root);
13168
13459
  const { manifest: reconciledManifest, result: rr } = reconcileComponents(project.root, manifest);
13169
13460
  reconcileResult = rr;
13170
13461
  if (!dryRun) {
@@ -13347,7 +13638,7 @@ async function syncCommand(options = {}) {
13347
13638
  // src/commands/migrate.ts
13348
13639
  import chalk31 from "chalk";
13349
13640
  import ora10 from "ora";
13350
- import { existsSync as existsSync27, mkdirSync as mkdirSync8, cpSync, rmSync as rmSync6, writeFileSync as writeFileSync12, readFileSync as readFileSync18, readdirSync as readdirSync7 } from "fs";
13641
+ import { existsSync as existsSync27, mkdirSync as mkdirSync8, cpSync, rmSync as rmSync6, writeFileSync as writeFileSync12, readFileSync as readFileSync19, readdirSync as readdirSync9 } from "fs";
13351
13642
  import { join as join21 } from "path";
13352
13643
  function backupDir(projectRoot) {
13353
13644
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
@@ -13382,7 +13673,7 @@ function rollback(projectRoot) {
13382
13673
  const guard = guardPath(projectRoot);
13383
13674
  if (!existsSync27(guard)) return false;
13384
13675
  try {
13385
- const data = JSON.parse(readFileSync18(guard, "utf-8"));
13676
+ const data = JSON.parse(readFileSync19(guard, "utf-8"));
13386
13677
  const backup = data.backup;
13387
13678
  if (!existsSync27(backup)) return false;
13388
13679
  const uiBackup = join21(backup, "components-ui");
@@ -13433,7 +13724,7 @@ async function migrateAction(options) {
13433
13724
  }
13434
13725
  const provider = getComponentProvider();
13435
13726
  const managedIds = new Set(provider.listNames());
13436
- const files = readdirSync7(uiDir).filter((f) => f.endsWith(".tsx"));
13727
+ const files = readdirSync9(uiDir).filter((f) => f.endsWith(".tsx"));
13437
13728
  const migratable = files.map((f) => f.replace(".tsx", "")).filter((id) => managedIds.has(id));
13438
13729
  if (migratable.length === 0) {
13439
13730
  console.log(chalk31.green("All components are already up to date."));
@@ -13476,7 +13767,7 @@ Found ${migratable.length} component(s) to migrate:`));
13476
13767
  }
13477
13768
 
13478
13769
  // src/utils/update-notifier.ts
13479
- import { existsSync as existsSync28, mkdirSync as mkdirSync9, readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "fs";
13770
+ import { existsSync as existsSync28, mkdirSync as mkdirSync9, readFileSync as readFileSync20, writeFileSync as writeFileSync13 } from "fs";
13480
13771
  import { join as join22 } from "path";
13481
13772
  import { homedir } from "os";
13482
13773
  import chalk32 from "chalk";
@@ -13489,7 +13780,7 @@ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
13489
13780
  function readCache() {
13490
13781
  try {
13491
13782
  if (!existsSync28(CACHE_FILE)) return null;
13492
- const raw = readFileSync19(CACHE_FILE, "utf-8");
13783
+ const raw = readFileSync20(CACHE_FILE, "utf-8");
13493
13784
  return JSON.parse(raw);
13494
13785
  } catch (e) {
13495
13786
  if (DEBUG5) console.error("Failed to read update cache:", e);