@getcoherent/cli 0.6.12 → 0.6.14

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
@@ -1,3 +1,21 @@
1
+ import {
2
+ createAIProvider
3
+ } from "./chunk-25JRF5MA.js";
4
+ import {
5
+ autoFixCode,
6
+ checkDesignConsistency,
7
+ formatIssues,
8
+ validatePageQuality,
9
+ verifyIncrementalEdit
10
+ } from "./chunk-N6H73ROO.js";
11
+ import {
12
+ generateArchitecturePlan,
13
+ getPageGroup,
14
+ getPageType,
15
+ loadPlan,
16
+ routeToKey,
17
+ savePlan
18
+ } from "./chunk-WRDWFCQJ.js";
1
19
  import {
2
20
  CORE_CONSTRAINTS,
3
21
  DESIGN_QUALITY,
@@ -5,16 +23,10 @@ import {
5
23
  DESIGN_THINKING,
6
24
  INTERACTION_PATTERNS,
7
25
  VISUAL_DEPTH,
8
- generateArchitecturePlan,
9
26
  getDesignQualityForType,
10
- getPageGroup,
11
- getPageType,
12
27
  inferPageTypeFromRoute,
13
- loadPlan,
14
- routeToKey,
15
- savePlan,
16
28
  selectContextualRules
17
- } from "./chunk-CLPILU3Z.js";
29
+ } from "./chunk-5AHG4NNX.js";
18
30
  import {
19
31
  __require
20
32
  } from "./chunk-3RG5ZIWI.js";
@@ -2454,9 +2466,17 @@ function buildSharedComponentsListForClaude(manifest) {
2454
2466
  if (!manifest.shared || manifest.shared.length === 0) {
2455
2467
  return "No shared components yet. Register with: coherent components shared add <name> --type layout|section|widget";
2456
2468
  }
2457
- const order = { layout: 0, section: 1, widget: 2 };
2469
+ const order = {
2470
+ layout: 0,
2471
+ navigation: 1,
2472
+ "data-display": 2,
2473
+ form: 3,
2474
+ feedback: 4,
2475
+ section: 5,
2476
+ widget: 6
2477
+ };
2458
2478
  const sorted = [...manifest.shared].sort(
2459
- (a, b) => order[a.type] - order[b.type] || a.name.localeCompare(b.name)
2479
+ (a, b) => (order[a.type] ?? 9) - (order[b.type] ?? 9) || a.name.localeCompare(b.name)
2460
2480
  );
2461
2481
  return sorted.map((e) => {
2462
2482
  const used = e.usedIn.length === 0 ? "\u2014" : e.usedIn.length === 1 && e.usedIn[0] === "app/layout.tsx" ? "layout" : e.usedIn.length + " files";
@@ -2941,14 +2961,29 @@ function buildSharedComponentsList(manifest) {
2941
2961
  When you create reusable blocks (headers, footers, repeated sections),
2942
2962
  register them: coherent components shared add <Name> --type layout|section|widget`;
2943
2963
  }
2944
- const order = { layout: 0, section: 1, widget: 2 };
2964
+ const typeOrder = {
2965
+ layout: 0,
2966
+ navigation: 1,
2967
+ "data-display": 2,
2968
+ form: 3,
2969
+ feedback: 4,
2970
+ section: 5,
2971
+ widget: 6
2972
+ };
2945
2973
  const sorted = [...manifest.shared].sort(
2946
- (a, b) => order[a.type] - order[b.type] || a.name.localeCompare(b.name)
2974
+ (a, b) => (typeOrder[a.type] ?? 9) - (typeOrder[b.type] ?? 9) || a.name.localeCompare(b.name)
2947
2975
  );
2948
2976
  const lines = sorted.map((entry) => {
2949
2977
  const usedIn = entry.usedIn.length === 0 ? "(not used yet)" : entry.usedIn.length === 1 && entry.usedIn[0] === "app/layout.tsx" ? "app/layout.tsx (all pages)" : entry.usedIn.join(", ");
2950
- return `- ${entry.id} ${entry.name} (${entry.type}) \u2014 ${entry.file}
2951
- Used in: ${usedIn}`;
2978
+ const importPath = entry.file.replace(/^components\/shared\//, "").replace(/\.tsx$/, "");
2979
+ const parts = [
2980
+ `- ${entry.id} ${entry.name} (${entry.type})${entry.description ? ` \u2014 ${entry.description}` : ""}`,
2981
+ ` Import: import { ${entry.name} } from '@/components/shared/${importPath}'`
2982
+ ];
2983
+ if (entry.propsInterface) parts.push(` Props: ${entry.propsInterface}`);
2984
+ if (entry.usageExample) parts.push(` Usage: ${entry.usageExample}`);
2985
+ parts.push(` Used in: ${usedIn}`);
2986
+ return parts.join("\n");
2952
2987
  });
2953
2988
  return `Currently registered shared components:
2954
2989
 
@@ -3811,7 +3846,7 @@ async function createAppRouteGroupLayout(projectPath) {
3811
3846
  }
3812
3847
 
3813
3848
  // src/commands/chat.ts
3814
- import chalk14 from "chalk";
3849
+ import chalk13 from "chalk";
3815
3850
  import ora2 from "ora";
3816
3851
  import { resolve as resolve10, relative as relative2, join as join11 } from "path";
3817
3852
  import { existsSync as existsSync17, readFileSync as readFileSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync4 } from "fs";
@@ -3822,129 +3857,12 @@ import {
3822
3857
  CLI_VERSION as CLI_VERSION2,
3823
3858
  getTemplateForPageType as getTemplateForPageType2,
3824
3859
  loadManifest as loadManifest8,
3825
- saveManifest as saveManifest2
3860
+ saveManifest as saveManifest2,
3861
+ updateEntry
3826
3862
  } from "@getcoherent/core";
3827
3863
 
3828
3864
  // src/agents/modifier.ts
3829
- import chalk6 from "chalk";
3830
-
3831
- // src/utils/ai-provider.ts
3832
3865
  import chalk5 from "chalk";
3833
- function detectAIProvider() {
3834
- if (process.env.OPENAI_API_KEY || process.env.CURSOR_OPENAI_API_KEY) {
3835
- return "openai";
3836
- }
3837
- if (process.env.ANTHROPIC_API_KEY) {
3838
- return "claude";
3839
- }
3840
- return "claude";
3841
- }
3842
- function hasAnyAPIKey() {
3843
- return !!(process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY || process.env.CURSOR_OPENAI_API_KEY || process.env.GITHUB_COPILOT_OPENAI_API_KEY);
3844
- }
3845
- function getAPIKey(provider) {
3846
- switch (provider) {
3847
- case "openai":
3848
- return process.env.OPENAI_API_KEY || process.env.CURSOR_OPENAI_API_KEY || process.env.GITHUB_COPILOT_OPENAI_API_KEY;
3849
- case "claude":
3850
- return process.env.ANTHROPIC_API_KEY;
3851
- case "auto":
3852
- const detected = detectAIProvider();
3853
- return getAPIKey(detected);
3854
- default:
3855
- return void 0;
3856
- }
3857
- }
3858
- function showAPIKeyHelp() {
3859
- console.log(chalk5.red("\n\u274C No API key found\n"));
3860
- console.log("To use Coherent, you need an AI provider API key.\n");
3861
- console.log(chalk5.cyan("\u250C\u2500 Quick Setup \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
3862
- console.log(chalk5.cyan("\u2502 \u2502"));
3863
- console.log(chalk5.cyan("\u2502 Option 1: Claude (Anthropic) \u2502"));
3864
- console.log(chalk5.gray("\u2502 Get key: https://console.anthropic.com/ \u2502"));
3865
- console.log(chalk5.cyan("\u2502 \u2502"));
3866
- console.log(chalk5.cyan("\u2502 Copy and run this command: \u2502"));
3867
- console.log(chalk5.cyan("\u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502"));
3868
- console.log(chalk5.white('\u2502 \u2502 echo "ANTHROPIC_API_KEY=sk-..." > .env \u2502 \u2502'));
3869
- console.log(chalk5.cyan("\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502"));
3870
- console.log(chalk5.cyan("\u2502 \u2502"));
3871
- console.log(chalk5.cyan("\u2502 Option 2: OpenAI (ChatGPT) \u2502"));
3872
- console.log(chalk5.gray("\u2502 Get key: https://platform.openai.com/ \u2502"));
3873
- console.log(chalk5.cyan("\u2502 \u2502"));
3874
- console.log(chalk5.cyan("\u2502 Copy and run this command: \u2502"));
3875
- console.log(chalk5.cyan("\u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502"));
3876
- console.log(chalk5.white('\u2502 \u2502 echo "OPENAI_API_KEY=sk-..." > .env \u2502 \u2502'));
3877
- console.log(chalk5.cyan("\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502"));
3878
- console.log(chalk5.cyan("\u2502 \u2502"));
3879
- console.log(chalk5.cyan("\u2502 Then run: coherent init \u2502"));
3880
- console.log(chalk5.cyan("\u2502 \u2502"));
3881
- console.log(chalk5.cyan("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n"));
3882
- console.log(chalk5.gray("Note: If using Cursor, your CURSOR_OPENAI_API_KEY"));
3883
- console.log(chalk5.gray("will be detected automatically.\n"));
3884
- }
3885
- async function createAIProvider(preferredProvider = "auto", config2) {
3886
- if (!hasAnyAPIKey() && !config2?.apiKey) {
3887
- showAPIKeyHelp();
3888
- throw new Error("API key required");
3889
- }
3890
- if (preferredProvider !== "auto") {
3891
- const apiKey2 = config2?.apiKey || getAPIKey(preferredProvider);
3892
- if (!apiKey2) {
3893
- const providerName = preferredProvider === "openai" ? "OpenAI" : "Anthropic Claude";
3894
- const envVar = preferredProvider === "openai" ? "OPENAI_API_KEY" : "ANTHROPIC_API_KEY";
3895
- showAPIKeyHelp();
3896
- throw new Error(`${providerName} API key not found.
3897
- Please set ${envVar} in your environment or .env file.`);
3898
- }
3899
- if (preferredProvider === "openai") {
3900
- try {
3901
- const { OpenAIClient } = await import("./openai-provider-XUI7ZHUR.js");
3902
- return await OpenAIClient.create(apiKey2, config2?.model);
3903
- } catch (error) {
3904
- if (error.message?.includes("not installed")) {
3905
- throw error;
3906
- }
3907
- throw new Error(
3908
- `OpenAI provider requires "openai" package. Install it with:
3909
- npm install openai
3910
- Or use Claude provider instead.
3911
- Error: ${error.message}`
3912
- );
3913
- }
3914
- } else {
3915
- const { ClaudeClient } = await import("./claude-BZ3HSBD3.js");
3916
- return ClaudeClient.create(apiKey2, config2?.model);
3917
- }
3918
- }
3919
- const provider = detectAIProvider();
3920
- const apiKey = config2?.apiKey || getAPIKey(provider);
3921
- if (!apiKey) {
3922
- showAPIKeyHelp();
3923
- throw new Error("API key required");
3924
- }
3925
- switch (provider) {
3926
- case "openai":
3927
- try {
3928
- const { OpenAIClient } = await import("./openai-provider-XUI7ZHUR.js");
3929
- return await OpenAIClient.create(apiKey, config2?.model);
3930
- } catch (error) {
3931
- if (error.message?.includes("not installed")) {
3932
- throw error;
3933
- }
3934
- throw new Error(
3935
- `OpenAI provider requires "openai" package. Install it with:
3936
- npm install openai
3937
- Or use Claude provider instead.
3938
- Error: ${error.message}`
3939
- );
3940
- }
3941
- case "claude":
3942
- const { ClaudeClient } = await import("./claude-BZ3HSBD3.js");
3943
- return ClaudeClient.create(apiKey, config2?.model);
3944
- default:
3945
- throw new Error(`Unsupported AI provider: ${provider}`);
3946
- }
3947
- }
3948
3866
 
3949
3867
  // src/agents/page-templates.ts
3950
3868
  var PAGE_TEMPLATES = {
@@ -4246,13 +4164,14 @@ async function parseModification(message, context, provider = "auto", options) {
4246
4164
  enhancedMessage = expandPageRequest(pageName, message);
4247
4165
  if (enhancedMessage !== message) {
4248
4166
  isExpandedPageRequest = true;
4249
- console.log(chalk6.cyan("\u{1F4A1} Expanding request with best practices..."));
4167
+ console.log(chalk5.cyan("\u{1F4A1} Expanding request with best practices..."));
4250
4168
  }
4251
4169
  }
4252
4170
  }
4253
4171
  const prompt = buildModificationPrompt(enhancedMessage, context.config, componentRegistry, {
4254
4172
  isExpandedPageRequest,
4255
- sharedComponentsSummary: options?.sharedComponentsSummary
4173
+ sharedComponentsSummary: options?.sharedComponentsSummary,
4174
+ tieredComponentsPrompt: options?.tieredComponentsPrompt
4256
4175
  });
4257
4176
  const raw = await ai.parseModification(prompt);
4258
4177
  const requestsArray = Array.isArray(raw) ? raw : raw?.requests ?? [];
@@ -4318,7 +4237,14 @@ Rules:
4318
4237
  function buildModificationPrompt(message, config2, componentRegistry, options) {
4319
4238
  const now = (/* @__PURE__ */ new Date()).toISOString();
4320
4239
  const expandedHint = options?.isExpandedPageRequest === true ? "\nIMPORTANT: The user request has been expanded with best practices. Use ALL the details provided when generating sections and content.\n\n" : "";
4321
- const sharedSection = options?.sharedComponentsSummary ? `
4240
+ const sharedSection = options?.tieredComponentsPrompt ? `
4241
+
4242
+ ## SHARED COMPONENTS (MANDATORY REUSE)
4243
+
4244
+ ${options.tieredComponentsPrompt}
4245
+
4246
+ For editing an existing shared component use type "modify-layout-block" with target "CID-XXX" or name.
4247
+ ` : options?.sharedComponentsSummary ? `
4322
4248
 
4323
4249
  ## SHARED COMPONENTS (MANDATORY REUSE)
4324
4250
 
@@ -4594,8 +4520,10 @@ Return valid JSON only, no markdown code fence. Use this shape:
4594
4520
  { "requests": [ ... array of ModificationRequest ... ], "uxRecommendations": "optional markdown or omit key" }
4595
4521
  Legacy: returning only a JSON array of requests is still accepted.`;
4596
4522
  }
4597
- function buildLightweightPagePrompt(pageName, route, styleContext, sharedComponentsSummary, pageType) {
4523
+ function buildLightweightPagePrompt(pageName, route, styleContext, sharedComponentsSummary, pageType, tieredComponentsPrompt) {
4598
4524
  const designConstraints = pageType ? getDesignQualityForType(pageType) : "";
4525
+ const sharedNote = tieredComponentsPrompt || (sharedComponentsSummary ? `Available shared components:
4526
+ ${sharedComponentsSummary}` : "");
4599
4527
  return [
4600
4528
  `Generate complete pageCode for a page called "${pageName}" at route "${route}".`,
4601
4529
  `Output valid TSX with a default export React component.`,
@@ -4604,8 +4532,7 @@ function buildLightweightPagePrompt(pageName, route, styleContext, sharedCompone
4604
4532
  designConstraints,
4605
4533
  styleContext ? `Follow this style context:
4606
4534
  ${styleContext}` : "",
4607
- sharedComponentsSummary ? `Available shared components:
4608
- ${sharedComponentsSummary}` : ""
4535
+ sharedNote
4609
4536
  ].filter(Boolean).join("\n\n");
4610
4537
  }
4611
4538
  async function checkComponentReuse(requests, componentManager) {
@@ -4805,7 +4732,7 @@ import { appendFile } from "fs/promises";
4805
4732
  // src/utils/backup.ts
4806
4733
  import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync6, readdirSync, rmSync as rmSync2, statSync } from "fs";
4807
4734
  import { join as join9, relative, dirname as dirname4 } from "path";
4808
- import chalk7 from "chalk";
4735
+ import chalk6 from "chalk";
4809
4736
  var DEBUG = process.env.COHERENT_DEBUG === "1";
4810
4737
  var BACKUP_DIR = ".coherent/backups";
4811
4738
  var MAX_BACKUPS = 5;
@@ -4952,7 +4879,7 @@ function restoreDirectory(currentDir, backupRoot, projectRoot) {
4952
4879
  function logBackupCreated(backupPath) {
4953
4880
  if (backupPath) {
4954
4881
  const name = backupPath.split("/").pop();
4955
- console.log(chalk7.dim(` \u{1F4BE} Backup saved: .coherent/backups/${name}`));
4882
+ console.log(chalk6.dim(` \u{1F4BE} Backup saved: .coherent/backups/${name}`));
4956
4883
  }
4957
4884
  }
4958
4885
 
@@ -5028,1248 +4955,11 @@ function fixGlobalsCss(projectRoot, config2) {
5028
4955
  writeFileSync7(layoutPath, layoutContent, "utf-8");
5029
4956
  }
5030
4957
 
5031
- // src/utils/component-rules.ts
5032
- function extractJsxElementProps(code, openTagStart) {
5033
- let i = openTagStart;
5034
- if (code[i] !== "<") return null;
5035
- i++;
5036
- let braceDepth = 0;
5037
- let inSingleQuote = false;
5038
- let inDoubleQuote = false;
5039
- let inTemplateLiteral = false;
5040
- let escaped = false;
5041
- while (i < code.length) {
5042
- const ch = code[i];
5043
- if (escaped) {
5044
- escaped = false;
5045
- i++;
5046
- continue;
5047
- }
5048
- if (ch === "\\" && (inSingleQuote || inDoubleQuote || inTemplateLiteral)) {
5049
- escaped = true;
5050
- i++;
5051
- continue;
5052
- }
5053
- if (!inDoubleQuote && !inTemplateLiteral && ch === "'" && braceDepth > 0) {
5054
- inSingleQuote = !inSingleQuote;
5055
- } else if (!inSingleQuote && !inTemplateLiteral && ch === '"') {
5056
- inDoubleQuote = !inDoubleQuote;
5057
- } else if (!inSingleQuote && !inDoubleQuote && ch === "`") {
5058
- inTemplateLiteral = !inTemplateLiteral;
5059
- }
5060
- if (!inSingleQuote && !inDoubleQuote && !inTemplateLiteral) {
5061
- if (ch === "{") braceDepth++;
5062
- else if (ch === "}") braceDepth--;
5063
- else if (ch === ">" && braceDepth === 0) {
5064
- return code.slice(openTagStart, i + 1);
5065
- }
5066
- }
5067
- i++;
5068
- }
5069
- return null;
5070
- }
5071
- var NAV_STYLE_SIGNAL = /text-muted-foreground/;
5072
- var buttonMissingGhostVariant = {
5073
- id: "button-missing-ghost-variant",
5074
- component: "Button",
5075
- detect(code) {
5076
- const issues = [];
5077
- const buttonRe = /<Button\s/g;
5078
- let match;
5079
- while ((match = buttonRe.exec(code)) !== null) {
5080
- const props = extractJsxElementProps(code, match.index);
5081
- if (!props) continue;
5082
- if (/\bvariant\s*=/.test(props)) continue;
5083
- if (!NAV_STYLE_SIGNAL.test(props)) continue;
5084
- const line = code.slice(0, match.index).split("\n").length;
5085
- issues.push({
5086
- line,
5087
- type: "BUTTON_MISSING_VARIANT",
5088
- message: '<Button> with navigation-style classes (text-muted-foreground) but no variant \u2014 add variant="ghost"',
5089
- severity: "error"
5090
- });
5091
- }
5092
- return issues;
5093
- },
5094
- fix(code) {
5095
- let result = code;
5096
- let applied = false;
5097
- const buttonRe = /<Button\s/g;
5098
- let match;
5099
- let offset = 0;
5100
- while ((match = buttonRe.exec(code)) !== null) {
5101
- const adjustedIndex = match.index + offset;
5102
- const props = extractJsxElementProps(result, adjustedIndex);
5103
- if (!props) continue;
5104
- if (/\bvariant\s*=/.test(props)) continue;
5105
- if (!NAV_STYLE_SIGNAL.test(props)) continue;
5106
- const insertPos = adjustedIndex + "<Button".length;
5107
- const insertion = "\n" + getIndent(result, adjustedIndex) + ' variant="ghost"';
5108
- result = result.slice(0, insertPos) + insertion + result.slice(insertPos);
5109
- offset += insertion.length;
5110
- applied = true;
5111
- }
5112
- return {
5113
- code: result,
5114
- applied,
5115
- description: 'added variant="ghost" to Button with nav-style classes'
5116
- };
5117
- }
5118
- };
5119
- function getIndent(code, pos) {
5120
- const lineStart = code.lastIndexOf("\n", pos);
5121
- const lineContent = code.slice(lineStart + 1, pos);
5122
- const indentMatch = lineContent.match(/^(\s*)/);
5123
- return indentMatch ? indentMatch[1] : "";
5124
- }
5125
- var buttonWFullGhostJustify = {
5126
- id: "button-w-full-ghost-justify",
5127
- component: "Button",
5128
- detect(code) {
5129
- const issues = [];
5130
- const buttonRe = /<Button\s/g;
5131
- let match;
5132
- while ((match = buttonRe.exec(code)) !== null) {
5133
- const props = extractJsxElementProps(code, match.index);
5134
- if (!props) continue;
5135
- const hasGhost = /variant\s*=\s*["']ghost["']/.test(props);
5136
- const hasWFull = /w-full/.test(props);
5137
- const hasJustifyStart = /justify-start/.test(props);
5138
- if (hasGhost && hasWFull && !hasJustifyStart) {
5139
- issues.push({
5140
- line: 0,
5141
- type: "button-w-full-ghost-justify",
5142
- severity: "warning",
5143
- message: 'Button with w-full and variant="ghost" should include justify-start for proper text alignment'
5144
- });
5145
- }
5146
- }
5147
- return issues;
5148
- },
5149
- fix(code) {
5150
- let applied = false;
5151
- const result = code.replace(
5152
- /(<Button\s[^>]*variant\s*=\s*["']ghost["'][^>]*className\s*=\s*["'][^"']*)(w-full)([^"']*["'])/g,
5153
- (full, before, wFull, after) => {
5154
- if (full.includes("justify-start")) return full;
5155
- applied = true;
5156
- return `${before}${wFull} justify-start${after}`;
5157
- }
5158
- );
5159
- return {
5160
- code: result,
5161
- applied,
5162
- description: "Added justify-start to ghost Button with w-full"
5163
- };
5164
- }
5165
- };
5166
- var rules = [buttonMissingGhostVariant, buttonWFullGhostJustify];
5167
- function detectComponentIssues(code) {
5168
- const issues = [];
5169
- for (const rule of rules) {
5170
- issues.push(...rule.detect(code));
5171
- }
5172
- return issues;
5173
- }
5174
- function applyComponentRules(code) {
5175
- const fixes = [];
5176
- let result = code;
5177
- for (const rule of rules) {
5178
- const { code: fixed, applied, description } = rule.fix(result);
5179
- if (applied) {
5180
- result = fixed;
5181
- fixes.push(description);
5182
- }
5183
- }
5184
- return { code: result, fixes };
5185
- }
5186
-
5187
- // src/utils/quality-validator.ts
5188
- var RAW_COLOR_RE = /(?:(?:hover|focus|active|group-hover|focus-visible|focus-within):)?(?:bg|text|border|ring|outline|from|to|via)-(gray|blue|red|green|yellow|purple|pink|indigo|orange|slate|zinc|stone|neutral|emerald|teal|cyan|sky|violet|fuchsia|rose|amber|lime)-\d+/g;
5189
- var HEX_IN_CLASS_RE = /className="[^"]*#[0-9a-fA-F]{3,8}[^"]*"/g;
5190
- var TEXT_BASE_RE = /\btext-base\b/g;
5191
- var HEAVY_SHADOW_RE = /\bshadow-(md|lg|xl|2xl)\b/g;
5192
- var SM_BREAKPOINT_RE = /\bsm:/g;
5193
- var XL_BREAKPOINT_RE = /\bxl:/g;
5194
- var XXL_BREAKPOINT_RE = /\b2xl:/g;
5195
- var LARGE_CARD_TITLE_RE = /CardTitle[^>]*className="[^"]*text-(lg|xl|2xl)/g;
5196
- var RAW_BUTTON_RE = /<button\b/g;
5197
- var RAW_INPUT_RE = /<input\b/g;
5198
- var RAW_SELECT_RE = /<select\b/g;
5199
- var NATIVE_CHECKBOX_RE = /<input[^>]*type\s*=\s*["']checkbox["']/g;
5200
- var NATIVE_TABLE_RE = /<table\b/g;
5201
- var PLACEHOLDER_PATTERNS = [
5202
- />\s*Lorem ipsum\b/i,
5203
- />\s*Card content\s*</i,
5204
- />\s*Your (?:text|content) here\s*</i,
5205
- />\s*Description\s*</,
5206
- />\s*Title\s*</,
5207
- /placeholder\s*text/i
5208
- ];
5209
- var GENERIC_BUTTON_LABELS = />\s*(Submit|OK|Click here|Press here|Go)\s*</i;
5210
- var IMG_WITHOUT_ALT_RE = /<img\b(?![^>]*\balt\s*=)[^>]*>/g;
5211
- var INPUT_TAG_RE = /<(?:Input|input)\b[^>]*>/g;
5212
- var LABEL_FOR_RE = /<Label\b[^>]*htmlFor\s*=/;
5213
- function isInsideCommentOrString(line, matchIndex) {
5214
- const commentIdx = line.indexOf("//");
5215
- if (commentIdx !== -1 && commentIdx < matchIndex) return true;
5216
- let inSingle = false;
5217
- let inDouble = false;
5218
- let inTemplate = false;
5219
- for (let i = 0; i < matchIndex; i++) {
5220
- const ch = line[i];
5221
- const prev = i > 0 ? line[i - 1] : "";
5222
- if (prev === "\\") continue;
5223
- if (ch === "'" && !inDouble && !inTemplate) inSingle = !inSingle;
5224
- if (ch === '"' && !inSingle && !inTemplate) inDouble = !inDouble;
5225
- if (ch === "`" && !inSingle && !inDouble) inTemplate = !inTemplate;
5226
- }
5227
- return inSingle || inDouble || inTemplate;
5228
- }
5229
- function checkLines(code, pattern, type, message, severity, skipCommentsAndStrings = false) {
5230
- const issues = [];
5231
- const lines = code.split("\n");
5232
- let inBlockComment = false;
5233
- for (let i = 0; i < lines.length; i++) {
5234
- const line = lines[i];
5235
- if (skipCommentsAndStrings) {
5236
- if (inBlockComment) {
5237
- const endIdx = line.indexOf("*/");
5238
- if (endIdx !== -1) {
5239
- inBlockComment = false;
5240
- }
5241
- continue;
5242
- }
5243
- const blockStart = line.indexOf("/*");
5244
- if (blockStart !== -1 && !line.includes("*/")) {
5245
- inBlockComment = true;
5246
- continue;
5247
- }
5248
- let m;
5249
- pattern.lastIndex = 0;
5250
- while ((m = pattern.exec(line)) !== null) {
5251
- if (!isInsideCommentOrString(line, m.index)) {
5252
- issues.push({ line: i + 1, type, message, severity });
5253
- break;
5254
- }
5255
- }
5256
- } else {
5257
- pattern.lastIndex = 0;
5258
- if (pattern.test(line)) {
5259
- issues.push({ line: i + 1, type, message, severity });
5260
- }
5261
- }
5262
- }
5263
- return issues;
5264
- }
5265
- function validatePageQuality(code, validRoutes, pageType) {
5266
- const issues = [];
5267
- const allLines = code.split("\n");
5268
- const isTerminalContext = (lineNum) => {
5269
- const start = Math.max(0, lineNum - 20);
5270
- const nearby = allLines.slice(start, lineNum).join(" ");
5271
- if (/font-mono/.test(allLines[lineNum - 1] || "")) return true;
5272
- if (/bg-zinc-950|bg-zinc-900/.test(nearby) && /font-mono/.test(nearby)) return true;
5273
- return false;
5274
- };
5275
- issues.push(
5276
- ...checkLines(
5277
- code,
5278
- RAW_COLOR_RE,
5279
- "RAW_COLOR",
5280
- "Raw Tailwind color detected \u2014 use semantic tokens (bg-primary, text-muted-foreground, etc.)",
5281
- "error"
5282
- ).filter((issue) => !isTerminalContext(issue.line))
5283
- );
5284
- issues.push(
5285
- ...checkLines(
5286
- code,
5287
- HEX_IN_CLASS_RE,
5288
- "HEX_IN_CLASS",
5289
- "Hex color in className \u2014 use CSS variables via semantic tokens",
5290
- "error"
5291
- )
5292
- );
5293
- issues.push(
5294
- ...checkLines(code, TEXT_BASE_RE, "TEXT_BASE", "text-base detected \u2014 use text-sm as base font size", "warning")
5295
- );
5296
- issues.push(
5297
- ...checkLines(code, HEAVY_SHADOW_RE, "HEAVY_SHADOW", "Heavy shadow detected \u2014 use shadow-sm or none", "warning")
5298
- );
5299
- issues.push(
5300
- ...checkLines(
5301
- code,
5302
- SM_BREAKPOINT_RE,
5303
- "SM_BREAKPOINT",
5304
- "sm: breakpoint \u2014 consider if md:/lg: is sufficient",
5305
- "info"
5306
- )
5307
- );
5308
- issues.push(
5309
- ...checkLines(
5310
- code,
5311
- XL_BREAKPOINT_RE,
5312
- "XL_BREAKPOINT",
5313
- "xl: breakpoint \u2014 consider if md:/lg: is sufficient",
5314
- "info"
5315
- )
5316
- );
5317
- issues.push(
5318
- ...checkLines(
5319
- code,
5320
- XXL_BREAKPOINT_RE,
5321
- "XXL_BREAKPOINT",
5322
- "2xl: breakpoint \u2014 rarely needed, consider xl: instead",
5323
- "warning"
5324
- )
5325
- );
5326
- issues.push(
5327
- ...checkLines(
5328
- code,
5329
- LARGE_CARD_TITLE_RE,
5330
- "LARGE_CARD_TITLE",
5331
- "Large text on CardTitle \u2014 use text-sm font-medium",
5332
- "warning"
5333
- )
5334
- );
5335
- const codeLines = code.split("\n");
5336
- issues.push(
5337
- ...checkLines(
5338
- code,
5339
- RAW_BUTTON_RE,
5340
- "NATIVE_BUTTON",
5341
- "Native <button> \u2014 use Button from @/components/ui/button",
5342
- "error",
5343
- true
5344
- ).filter((issue) => {
5345
- const nearby = codeLines.slice(Math.max(0, issue.line - 1), issue.line + 5).join(" ");
5346
- if (nearby.includes("aria-label")) return false;
5347
- if (/onClick=\{.*copy/i.test(nearby)) return false;
5348
- return true;
5349
- })
5350
- );
5351
- issues.push(
5352
- ...checkLines(
5353
- code,
5354
- RAW_SELECT_RE,
5355
- "NATIVE_SELECT",
5356
- "Native <select> \u2014 use Select from @/components/ui/select",
5357
- "error",
5358
- true
5359
- )
5360
- );
5361
- issues.push(
5362
- ...checkLines(
5363
- code,
5364
- NATIVE_CHECKBOX_RE,
5365
- "NATIVE_CHECKBOX",
5366
- 'Native <input type="checkbox"> \u2014 use Switch or Checkbox from @/components/ui/switch or @/components/ui/checkbox',
5367
- "error",
5368
- true
5369
- )
5370
- );
5371
- issues.push(
5372
- ...checkLines(
5373
- code,
5374
- NATIVE_TABLE_RE,
5375
- "NATIVE_TABLE",
5376
- "Native <table> \u2014 use Table, TableHeader, TableBody, etc. from @/components/ui/table",
5377
- "warning",
5378
- true
5379
- )
5380
- );
5381
- const hasInputImport = /import\s.*Input.*from\s+['"]@\/components\/ui\//.test(code);
5382
- if (!hasInputImport) {
5383
- issues.push(
5384
- ...checkLines(
5385
- code,
5386
- RAW_INPUT_RE,
5387
- "RAW_INPUT",
5388
- "Raw <input> element \u2014 import and use Input from @/components/ui/input",
5389
- "warning",
5390
- true
5391
- )
5392
- );
5393
- }
5394
- for (const pattern of PLACEHOLDER_PATTERNS) {
5395
- const lines = code.split("\n");
5396
- for (let i = 0; i < lines.length; i++) {
5397
- if (pattern.test(lines[i])) {
5398
- issues.push({
5399
- line: i + 1,
5400
- type: "PLACEHOLDER",
5401
- message: "Placeholder content detected \u2014 use real contextual content",
5402
- severity: "error"
5403
- });
5404
- }
5405
- }
5406
- }
5407
- const hasGrid = /\bgrid\b/.test(code);
5408
- const hasResponsive = /\bmd:|lg:/.test(code);
5409
- if (hasGrid && !hasResponsive) {
5410
- issues.push({
5411
- line: 0,
5412
- type: "NO_RESPONSIVE",
5413
- message: "Grid layout without responsive breakpoints (md: or lg:)",
5414
- severity: "warning"
5415
- });
5416
- }
5417
- issues.push(
5418
- ...checkLines(
5419
- code,
5420
- IMG_WITHOUT_ALT_RE,
5421
- "MISSING_ALT",
5422
- '<img> without alt attribute \u2014 add descriptive alt or alt="" for decorative images',
5423
- "error"
5424
- )
5425
- );
5426
- issues.push(
5427
- ...checkLines(
5428
- code,
5429
- GENERIC_BUTTON_LABELS,
5430
- "GENERIC_BUTTON_TEXT",
5431
- 'Generic button text \u2014 use specific verb ("Save changes", "Delete account")',
5432
- "warning"
5433
- )
5434
- );
5435
- if (pageType !== "auth") {
5436
- const h1Matches = code.match(/<h1[\s>]/g);
5437
- if (!h1Matches || h1Matches.length === 0) {
5438
- issues.push({
5439
- line: 0,
5440
- type: "NO_H1",
5441
- message: "Page has no <h1> \u2014 every page should have exactly one h1 heading",
5442
- severity: "warning"
5443
- });
5444
- } else if (h1Matches.length > 1) {
5445
- issues.push({
5446
- line: 0,
5447
- type: "MULTIPLE_H1",
5448
- message: `Page has ${h1Matches.length} <h1> elements \u2014 use exactly one per page`,
5449
- severity: "warning"
5450
- });
5451
- }
5452
- }
5453
- const headingLevels = [...code.matchAll(/<h([1-6])[\s>]/g)].map((m) => parseInt(m[1]));
5454
- const hasCardContext = /\bCard\b|\bCardTitle\b|\bCardHeader\b/.test(code);
5455
- for (let i = 1; i < headingLevels.length; i++) {
5456
- if (headingLevels[i] > headingLevels[i - 1] + 1) {
5457
- issues.push({
5458
- line: 0,
5459
- type: "SKIPPED_HEADING",
5460
- message: `Heading level skipped: h${headingLevels[i - 1]} \u2192 h${headingLevels[i]} \u2014 don't skip levels`,
5461
- severity: hasCardContext ? "info" : "warning"
5462
- });
5463
- break;
5464
- }
5465
- }
5466
- const hasLabelImport = /import\s.*Label.*from\s+['"]@\/components\/ui\//.test(code);
5467
- const inputCount = (code.match(INPUT_TAG_RE) || []).length;
5468
- const labelForCount = (code.match(LABEL_FOR_RE) || []).length;
5469
- if (hasLabelImport && inputCount > 0 && labelForCount === 0) {
5470
- issues.push({
5471
- line: 0,
5472
- type: "MISSING_LABEL",
5473
- message: "Inputs found but no Label with htmlFor \u2014 every input must have a visible label",
5474
- severity: "error"
5475
- });
5476
- }
5477
- if (!hasLabelImport && inputCount > 0 && !/<label\b/i.test(code)) {
5478
- issues.push({
5479
- line: 0,
5480
- type: "MISSING_LABEL",
5481
- message: "Inputs found but no Label component \u2014 import Label and add htmlFor on each input",
5482
- severity: "error"
5483
- });
5484
- }
5485
- const hasPlaceholder = /placeholder\s*=/.test(code);
5486
- if (hasPlaceholder && inputCount > 0 && labelForCount === 0 && !/<label\b/i.test(code) && !/<Label\b/.test(code)) {
5487
- issues.push({
5488
- line: 0,
5489
- type: "PLACEHOLDER_ONLY_LABEL",
5490
- message: "Inputs use placeholder only \u2014 add visible Label with htmlFor (placeholder is not a substitute)",
5491
- severity: "error"
5492
- });
5493
- }
5494
- const hasInteractive = /<Button\b|<button\b|<a\b/.test(code);
5495
- const hasFocusVisible = /focus-visible:/.test(code);
5496
- const usesShadcnButton = /import\s.*Button.*from\s+['"]@\/components\/ui\//.test(code);
5497
- if (hasInteractive && !hasFocusVisible && !usesShadcnButton) {
5498
- issues.push({
5499
- line: 0,
5500
- type: "MISSING_FOCUS_VISIBLE",
5501
- message: "Interactive elements without focus-visible styles \u2014 add focus-visible:ring-2 focus-visible:ring-ring",
5502
- severity: "info"
5503
- });
5504
- }
5505
- const hasTableOrList = /<Table\b|<table\b|\.map\s*\(|<ul\b|<ol\b/.test(code);
5506
- const hasEmptyCheck = /\.length\s*[=!]==?\s*0|\.length\s*>\s*0|\.length\s*<\s*1|No\s+\w+\s+found|empty|no results|EmptyState|empty state/i.test(
5507
- code
5508
- );
5509
- if (hasTableOrList && !hasEmptyCheck) {
5510
- issues.push({
5511
- line: 0,
5512
- type: "NO_EMPTY_STATE",
5513
- message: "List/table/grid without empty state handling \u2014 add friendly message + primary action",
5514
- severity: "warning"
5515
- });
5516
- }
5517
- const hasDataFetching = /fetch\s*\(|useQuery|useSWR|useEffect\s*\([^)]*fetch|getData|loadData/i.test(code);
5518
- const hasLoadingPattern = /skeleton|Skeleton|spinner|Spinner|isLoading|loading|Loading/.test(code);
5519
- if (hasDataFetching && !hasLoadingPattern) {
5520
- issues.push({
5521
- line: 0,
5522
- type: "NO_LOADING_STATE",
5523
- message: "Page with data fetching but no loading/skeleton pattern \u2014 add skeleton or spinner",
5524
- severity: "warning"
5525
- });
5526
- }
5527
- const hasGenericError = /Something went wrong|"Error"|'Error'|>Error<\//.test(code) || /error\.message\s*\|\|\s*["']Error["']/.test(code);
5528
- if (hasGenericError) {
5529
- issues.push({
5530
- line: 0,
5531
- type: "EMPTY_ERROR_MESSAGE",
5532
- message: "Generic error message detected \u2014 use what happened + why + what to do next",
5533
- severity: "warning"
5534
- });
5535
- }
5536
- const hasDestructive = /variant\s*=\s*["']destructive["']|Delete|Remove/.test(code);
5537
- const hasConfirm = /AlertDialog|Dialog.*confirm|confirm\s*\(|onConfirm|are you sure/i.test(code);
5538
- if (hasDestructive && !hasConfirm) {
5539
- issues.push({
5540
- line: 0,
5541
- type: "DESTRUCTIVE_NO_CONFIRM",
5542
- message: "Destructive action without confirmation dialog \u2014 add confirm before execution",
5543
- severity: "warning"
5544
- });
5545
- }
5546
- const hasFormSubmit = /<form\b|onSubmit|type\s*=\s*["']submit["']/.test(code);
5547
- const hasFeedback = /toast|success|error|Saved|Saving|saving|setError|setSuccess/i.test(code);
5548
- if (hasFormSubmit && !hasFeedback) {
5549
- issues.push({
5550
- line: 0,
5551
- type: "FORM_NO_FEEDBACK",
5552
- message: 'Form with submit but no success/error feedback pattern \u2014 add "Saving..." then "Saved" or error',
5553
- severity: "info"
5554
- });
5555
- }
5556
- const hasNav = /<nav\b|NavLink|navigation|sidebar.*link|Sidebar.*link/i.test(code);
5557
- const hasActiveState = /pathname|active|current|aria-current|data-active/.test(code);
5558
- if (hasNav && !hasActiveState) {
5559
- issues.push({
5560
- line: 0,
5561
- type: "NAV_NO_ACTIVE_STATE",
5562
- message: "Navigation without active/current page indicator \u2014 add active state for current route",
5563
- severity: "info"
5564
- });
5565
- }
5566
- if (validRoutes && validRoutes.length > 0) {
5567
- const routeSet = new Set(validRoutes);
5568
- routeSet.add("#");
5569
- const lines = code.split("\n");
5570
- const linkHrefRe = /href\s*=\s*["'](\/[a-z0-9/-]*)["']/gi;
5571
- for (let i = 0; i < lines.length; i++) {
5572
- let match;
5573
- while ((match = linkHrefRe.exec(lines[i])) !== null) {
5574
- const target = match[1];
5575
- if (target === "/" || target.startsWith("/design-system") || target.startsWith("/api") || target.startsWith("/#"))
5576
- continue;
5577
- if (!routeSet.has(target)) {
5578
- issues.push({
5579
- line: i + 1,
5580
- type: "BROKEN_INTERNAL_LINK",
5581
- message: `Link to "${target}" \u2014 route does not exist in project`,
5582
- severity: "warning"
5583
- });
5584
- }
5585
- }
5586
- }
5587
- }
5588
- const linkBlockRe = /<(?:Link|a)\b[^>]*>[\s\S]*?<\/(?:Link|a)>/g;
5589
- let linkMatch;
5590
- while ((linkMatch = linkBlockRe.exec(code)) !== null) {
5591
- const block = linkMatch[0];
5592
- if (/<(?:Button|button)\b/.test(block) && !/asChild/.test(block)) {
5593
- issues.push({
5594
- line: 0,
5595
- type: "NESTED_INTERACTIVE",
5596
- message: "Button inside Link without asChild \u2014 causes DOM nesting error. Use <Button asChild><Link>...</Link></Button> instead",
5597
- severity: "error"
5598
- });
5599
- break;
5600
- }
5601
- }
5602
- const nestedAnchorRe = /<a\b[^>]*>[\s\S]*?<a\b/;
5603
- if (nestedAnchorRe.test(code)) {
5604
- issues.push({
5605
- line: 0,
5606
- type: "NESTED_INTERACTIVE",
5607
- message: "Nested <a> tags \u2014 causes DOM nesting error. Remove inner anchor or restructure",
5608
- severity: "error"
5609
- });
5610
- }
5611
- const linkWithoutHrefRe = /<(?:Link|a)\b(?![^>]*\bhref\s*=)[^>]*>/g;
5612
- let linkNoHrefMatch;
5613
- while ((linkNoHrefMatch = linkWithoutHrefRe.exec(code)) !== null) {
5614
- const matchLine = code.slice(0, linkNoHrefMatch.index).split("\n").length;
5615
- issues.push({
5616
- line: matchLine,
5617
- type: "LINK_MISSING_HREF",
5618
- message: "<Link> or <a> without href prop \u2014 causes Next.js runtime error. Add href attribute.",
5619
- severity: "error"
5620
- });
5621
- }
5622
- issues.push(...detectComponentIssues(code));
5623
- return issues;
5624
- }
5625
- function resolveHref(linkText, context) {
5626
- if (!context) return "/";
5627
- const text = linkText.trim().toLowerCase();
5628
- if (context.linkMap) {
5629
- for (const [label, route] of Object.entries(context.linkMap)) {
5630
- if (label.toLowerCase() === text) return route;
5631
- }
5632
- }
5633
- if (context.knownRoutes) {
5634
- const cleaned = text.replace(/^(back\s+to|go\s+to|view\s+all|see\s+all|return\s+to)\s+/i, "").trim();
5635
- for (const route of context.knownRoutes) {
5636
- const slug = route.split("/").filter(Boolean).pop() || "";
5637
- const routeName = slug.replace(/[-_]/g, " ");
5638
- if (routeName && cleaned === routeName) return route;
5639
- }
5640
- }
5641
- return "/";
5642
- }
5643
- function replaceRawColors(classes, colorMap) {
5644
- let changed = false;
5645
- let result = classes;
5646
- const accentColorRe = /\b((?:(?:hover|focus|active|group-hover|focus-visible|focus-within):)?)(bg|text|border|ring|outline|from|to|via)-(emerald|blue|violet|indigo|purple|teal|cyan|sky|rose|amber|red|green|yellow|pink|orange|fuchsia|lime)-(\d+)\b/g;
5647
- result = result.replace(accentColorRe, (m, statePrefix, prefix, color, shade) => {
5648
- const bare = m.replace(statePrefix, "");
5649
- if (colorMap[bare]) {
5650
- changed = true;
5651
- return statePrefix + colorMap[bare];
5652
- }
5653
- const n = parseInt(shade);
5654
- const isDestructive = color === "red";
5655
- if (prefix === "bg") {
5656
- if (n >= 500 && n <= 700) {
5657
- changed = true;
5658
- return statePrefix + (isDestructive ? "bg-destructive" : "bg-primary");
5659
- }
5660
- if (n >= 100 && n <= 200) {
5661
- changed = true;
5662
- return statePrefix + (isDestructive ? "bg-destructive/10" : "bg-primary/10");
5663
- }
5664
- if (n >= 300 && n <= 400) {
5665
- changed = true;
5666
- return statePrefix + (isDestructive ? "bg-destructive/20" : "bg-primary/20");
5667
- }
5668
- if (n >= 800) {
5669
- changed = true;
5670
- return statePrefix + "bg-muted";
5671
- }
5672
- }
5673
- if (prefix === "text") {
5674
- if (n >= 400 && n <= 600) {
5675
- changed = true;
5676
- return statePrefix + (isDestructive ? "text-destructive" : "text-primary");
5677
- }
5678
- if (n >= 100 && n <= 300) {
5679
- changed = true;
5680
- return statePrefix + "text-foreground";
5681
- }
5682
- if (n >= 700) {
5683
- changed = true;
5684
- return statePrefix + "text-foreground";
5685
- }
5686
- }
5687
- if (prefix === "border" || prefix === "ring" || prefix === "outline") {
5688
- changed = true;
5689
- return statePrefix + (isDestructive ? `${prefix}-destructive` : `${prefix}-primary`);
5690
- }
5691
- if (prefix === "from" || prefix === "to" || prefix === "via") {
5692
- changed = true;
5693
- if (n >= 100 && n <= 300)
5694
- return statePrefix + (isDestructive ? `${prefix}-destructive/20` : `${prefix}-primary/20`);
5695
- return statePrefix + (isDestructive ? `${prefix}-destructive` : `${prefix}-primary`);
5696
- }
5697
- return m;
5698
- });
5699
- const neutralColorRe = /\b((?:(?:hover|focus|active|group-hover|focus-visible|focus-within):)?)(bg|text|border|ring|outline)-(zinc|slate|gray|neutral|stone)-(\d+)\b/g;
5700
- result = result.replace(neutralColorRe, (m, statePrefix, prefix, _color, shade) => {
5701
- const bare = m.replace(statePrefix, "");
5702
- if (colorMap[bare]) {
5703
- changed = true;
5704
- return statePrefix + colorMap[bare];
5705
- }
5706
- const n = parseInt(shade);
5707
- if (prefix === "bg") {
5708
- if (n >= 800) {
5709
- changed = true;
5710
- return statePrefix + "bg-background";
5711
- }
5712
- if (n >= 100 && n <= 300) {
5713
- changed = true;
5714
- return statePrefix + "bg-muted";
5715
- }
5716
- }
5717
- if (prefix === "text") {
5718
- if (n >= 100 && n <= 300) {
5719
- changed = true;
5720
- return statePrefix + "text-foreground";
5721
- }
5722
- if (n >= 400 && n <= 600) {
5723
- changed = true;
5724
- return statePrefix + "text-muted-foreground";
5725
- }
5726
- }
5727
- if (prefix === "border" || prefix === "ring" || prefix === "outline") {
5728
- changed = true;
5729
- return statePrefix + `${prefix === "border" ? "border-border" : `${prefix}-ring`}`;
5730
- }
5731
- return m;
5732
- });
5733
- return { result, changed };
5734
- }
5735
- async function autoFixCode(code, context) {
5736
- const fixes = [];
5737
- let fixed = code;
5738
- const beforeQuoteFix = fixed;
5739
- fixed = fixed.replace(/\\'(\s*[}\],])/g, "'$1");
5740
- fixed = fixed.replace(/(:\s*'.+)\\'(\s*)$/gm, "$1'$2");
5741
- if (fixed !== beforeQuoteFix) {
5742
- fixes.push("fixed escaped closing quotes in strings");
5743
- }
5744
- const beforeEntityFix = fixed;
5745
- const isInsideAttrValue = (line, idx) => {
5746
- let inQuote = false;
5747
- let inAttr = false;
5748
- for (let i = 0; i < idx; i++) {
5749
- if (line[i] === "=" && line[i + 1] === '"') {
5750
- inAttr = true;
5751
- inQuote = true;
5752
- i++;
5753
- } else if (inAttr && line[i] === '"') {
5754
- inAttr = false;
5755
- inQuote = false;
5756
- }
5757
- }
5758
- return inQuote;
5759
- };
5760
- fixed = fixed.split("\n").map((line) => {
5761
- let l = line;
5762
- l = l.replace(/&lt;=/g, (m, offset) => isInsideAttrValue(line, offset) ? m : "<=");
5763
- l = l.replace(/&gt;=/g, (m, offset) => isInsideAttrValue(line, offset) ? m : ">=");
5764
- l = l.replace(/&amp;&amp;/g, (m, offset) => isInsideAttrValue(line, offset) ? m : "&&");
5765
- l = l.replace(
5766
- /([\w)\]])\s*&lt;\s*([\w(])/g,
5767
- (m, p1, p2, offset) => isInsideAttrValue(line, offset) ? m : `${p1} < ${p2}`
5768
- );
5769
- l = l.replace(
5770
- /([\w)\]])\s*&gt;\s*([\w(])/g,
5771
- (m, p1, p2, offset) => isInsideAttrValue(line, offset) ? m : `${p1} > ${p2}`
5772
- );
5773
- return l;
5774
- }).join("\n");
5775
- if (fixed !== beforeEntityFix) {
5776
- fixes.push("Fixed syntax issues");
5777
- }
5778
- const beforeLtFix = fixed;
5779
- fixed = fixed.replace(/>([^<{}\n]*)<(\d)/g, ">$1&lt;$2");
5780
- fixed = fixed.replace(/>([^<{}\n]*)<([^/a-zA-Z!{>\n])/g, ">$1&lt;$2");
5781
- if (fixed !== beforeLtFix) {
5782
- fixes.push("escaped < in JSX text content");
5783
- }
5784
- if (/className="[^"]*\btext-base\b[^"]*"/.test(fixed)) {
5785
- fixed = fixed.replace(/className="([^"]*)\btext-base\b([^"]*)"/g, 'className="$1text-sm$2"');
5786
- fixes.push("text-base \u2192 text-sm");
5787
- }
5788
- if (/CardTitle[^>]*className="[^"]*text-(lg|xl|2xl)/.test(fixed)) {
5789
- fixed = fixed.replace(/(CardTitle[^>]*className="[^"]*)text-(lg|xl|2xl)\b/g, "$1");
5790
- fixes.push("large text in CardTitle \u2192 removed");
5791
- }
5792
- if (/className="[^"]*\bshadow-(md|lg|xl|2xl)\b[^"]*"/.test(fixed)) {
5793
- fixed = fixed.replace(/className="([^"]*)\bshadow-(md|lg|xl|2xl)\b([^"]*)"/g, 'className="$1shadow-sm$3"');
5794
- fixes.push("heavy shadow \u2192 shadow-sm");
5795
- }
5796
- const hasHooks = /\b(useState|useEffect|useRef|useCallback|useMemo|useReducer|useContext)\b/.test(fixed);
5797
- const hasEvents = /\b(onClick|onChange|onSubmit|onBlur|onFocus|onKeyDown|onKeyUp|onMouseEnter|onMouseLeave|onScroll|onInput)\s*[={]/.test(
5798
- fixed
5799
- );
5800
- const hasUseClient = /^['"]use client['"]/.test(fixed.trim());
5801
- if ((hasHooks || hasEvents) && !hasUseClient) {
5802
- fixed = `'use client'
5803
-
5804
- ${fixed}`;
5805
- fixes.push('added "use client" (client features detected)');
5806
- }
5807
- if (/^['"]use client['"]/.test(fixed.trim()) && /\bexport\s+const\s+metadata\s*:\s*Metadata\s*=\s*\{/.test(fixed)) {
5808
- const metaMatch = fixed.match(/\bexport\s+const\s+metadata\s*:\s*Metadata\s*=\s*\{/);
5809
- if (metaMatch) {
5810
- const start = fixed.indexOf(metaMatch[0]);
5811
- const open = fixed.indexOf("{", start);
5812
- let depth = 1, i = open + 1;
5813
- while (i < fixed.length && depth > 0) {
5814
- if (fixed[i] === "{") depth++;
5815
- else if (fixed[i] === "}") depth--;
5816
- i++;
5817
- }
5818
- const tail = fixed.slice(i);
5819
- const semi = tail.match(/^\s*;/);
5820
- const removeEnd = semi ? i + (semi.index + semi[0].length) : i;
5821
- fixed = (fixed.slice(0, start) + fixed.slice(removeEnd)).replace(/\n{3,}/g, "\n\n").trim();
5822
- fixes.push('removed metadata export (conflicts with "use client")');
5823
- }
5824
- }
5825
- const lines = fixed.split("\n");
5826
- let hasReplacedButton = false;
5827
- for (let i = 0; i < lines.length; i++) {
5828
- if (!/<button\b/.test(lines[i])) continue;
5829
- if (lines[i].includes("aria-label")) continue;
5830
- if (/onClick=\{.*copy/i.test(lines[i])) continue;
5831
- const block = lines.slice(i, i + 5).join(" ");
5832
- if (block.includes("aria-label") || /onClick=\{.*copy/i.test(block)) continue;
5833
- lines[i] = lines[i].replace(/<button\b/g, "<Button");
5834
- hasReplacedButton = true;
5835
- }
5836
- if (hasReplacedButton) {
5837
- fixed = lines.join("\n");
5838
- fixed = fixed.replace(/<\/button>/g, (_match, _offset) => {
5839
- return "</Button>";
5840
- });
5841
- const openCount = (fixed.match(/<Button\b/g) || []).length;
5842
- const closeCount = (fixed.match(/<\/Button>/g) || []).length;
5843
- if (closeCount > openCount) {
5844
- let excess = closeCount - openCount;
5845
- fixed = fixed.replace(/<\/Button>/g, (m) => {
5846
- if (excess > 0) {
5847
- excess--;
5848
- return "</button>";
5849
- }
5850
- return m;
5851
- });
5852
- }
5853
- const hasButtonImport = /import\s.*\bButton\b.*from\s+['"]@\/components\/ui\/button['"]/.test(fixed);
5854
- if (!hasButtonImport) {
5855
- const lastImportIdx = fixed.lastIndexOf("\nimport ");
5856
- if (lastImportIdx !== -1) {
5857
- const lineEnd = fixed.indexOf("\n", lastImportIdx + 1);
5858
- fixed = fixed.slice(0, lineEnd + 1) + "import { Button } from '@/components/ui/button'\n" + fixed.slice(lineEnd + 1);
5859
- } else {
5860
- const insertAfter = hasUseClient ? fixed.indexOf("\n") + 1 : 0;
5861
- fixed = fixed.slice(0, insertAfter) + "import { Button } from '@/components/ui/button'\n" + fixed.slice(insertAfter);
5862
- }
5863
- }
5864
- fixes.push("<button> \u2192 <Button> (with import)");
5865
- }
5866
- const colorMap = {
5867
- "bg-zinc-950": "bg-background",
5868
- "bg-zinc-900": "bg-background",
5869
- "bg-slate-950": "bg-background",
5870
- "bg-slate-900": "bg-background",
5871
- "bg-gray-950": "bg-background",
5872
- "bg-gray-900": "bg-background",
5873
- "bg-zinc-800": "bg-muted",
5874
- "bg-slate-800": "bg-muted",
5875
- "bg-gray-800": "bg-muted",
5876
- "bg-zinc-100": "bg-muted",
5877
- "bg-slate-100": "bg-muted",
5878
- "bg-gray-100": "bg-muted",
5879
- "bg-white": "bg-background",
5880
- "bg-black": "bg-background",
5881
- "text-white": "text-foreground",
5882
- "text-black": "text-foreground",
5883
- "text-zinc-100": "text-foreground",
5884
- "text-zinc-200": "text-foreground",
5885
- "text-slate-100": "text-foreground",
5886
- "text-gray-100": "text-foreground",
5887
- "text-zinc-400": "text-muted-foreground",
5888
- "text-zinc-500": "text-muted-foreground",
5889
- "text-slate-400": "text-muted-foreground",
5890
- "text-slate-500": "text-muted-foreground",
5891
- "text-gray-400": "text-muted-foreground",
5892
- "text-gray-500": "text-muted-foreground",
5893
- "border-zinc-700": "border-border",
5894
- "border-zinc-800": "border-border",
5895
- "border-slate-700": "border-border",
5896
- "border-gray-700": "border-border",
5897
- "border-zinc-200": "border-border",
5898
- "border-slate-200": "border-border",
5899
- "border-gray-200": "border-border"
5900
- };
5901
- const isCodeContext = (classes) => /\bfont-mono\b/.test(classes) || /\bbg-zinc-950\b/.test(classes) || /\bbg-zinc-900\b/.test(classes);
5902
- const isInsideTerminalBlock = (offset) => {
5903
- const preceding = fixed.slice(Math.max(0, offset - 600), offset);
5904
- if (!/(bg-zinc-950|bg-zinc-900)/.test(preceding)) return false;
5905
- if (!/font-mono/.test(preceding)) return false;
5906
- const lastClose = Math.max(preceding.lastIndexOf("</div>"), preceding.lastIndexOf("</section>"));
5907
- const lastTerminal = Math.max(preceding.lastIndexOf("bg-zinc-950"), preceding.lastIndexOf("bg-zinc-900"));
5908
- return lastTerminal > lastClose;
5909
- };
5910
- let hadColorFix = false;
5911
- fixed = fixed.replace(/className="([^"]*)"/g, (fullMatch, classes, offset) => {
5912
- if (isCodeContext(classes)) return fullMatch;
5913
- if (isInsideTerminalBlock(offset)) return fullMatch;
5914
- const { result, changed } = replaceRawColors(classes, colorMap);
5915
- if (changed) hadColorFix = true;
5916
- if (result !== classes) return `className="${result}"`;
5917
- return fullMatch;
5918
- });
5919
- fixed = fixed.replace(/(?:cn|clsx|cva)\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g, (fullMatch, args) => {
5920
- const replaced = args.replace(/"([^"]*)"/g, (_qm, inner) => {
5921
- const { result, changed } = replaceRawColors(inner, colorMap);
5922
- if (changed) hadColorFix = true;
5923
- return `"${result}"`;
5924
- });
5925
- if (replaced !== args) return fullMatch.replace(args, replaced);
5926
- return fullMatch;
5927
- });
5928
- fixed = fixed.replace(/className='([^']*)'/g, (fullMatch, classes, offset) => {
5929
- if (isCodeContext(classes)) return fullMatch;
5930
- if (isInsideTerminalBlock(offset)) return fullMatch;
5931
- const { result, changed } = replaceRawColors(classes, colorMap);
5932
- if (changed) hadColorFix = true;
5933
- if (result !== classes) return `className='${result}'`;
5934
- return fullMatch;
5935
- });
5936
- fixed = fixed.replace(/className=\{`([^`]*)`\}/g, (fullMatch, inner) => {
5937
- const { result, changed } = replaceRawColors(inner, colorMap);
5938
- if (changed) hadColorFix = true;
5939
- if (result !== inner) return `className={\`${result}\`}`;
5940
- return fullMatch;
5941
- });
5942
- if (hadColorFix) fixes.push("raw colors \u2192 semantic tokens");
5943
- const selectRe = /<select\b[^>]*>([\s\S]*?)<\/select>/g;
5944
- let hadSelectFix = false;
5945
- fixed = fixed.replace(selectRe, (_match, inner) => {
5946
- const options = [];
5947
- const optionRe = /<option\s+value="([^"]*)"[^>]*>([^<]*)<\/option>/g;
5948
- let optMatch;
5949
- while ((optMatch = optionRe.exec(inner)) !== null) {
5950
- options.push({ value: optMatch[1], label: optMatch[2] });
5951
- }
5952
- if (options.length === 0) return _match;
5953
- hadSelectFix = true;
5954
- const items = options.map((o) => ` <SelectItem value="${o.value}">${o.label}</SelectItem>`).join("\n");
5955
- return `<Select>
5956
- <SelectTrigger>
5957
- <SelectValue placeholder="Select..." />
5958
- </SelectTrigger>
5959
- <SelectContent>
5960
- ${items}
5961
- </SelectContent>
5962
- </Select>`;
5963
- });
5964
- if (hadSelectFix) {
5965
- fixes.push("<select> \u2192 shadcn Select");
5966
- const selectImport = `import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@/components/ui/select'`;
5967
- if (!/from\s+['"]@\/components\/ui\/select['"]/.test(fixed)) {
5968
- const replaced = fixed.replace(
5969
- /(import\s+\{[^}]*\}\s+from\s+['"]@\/components\/ui\/[^'"]+['"])/,
5970
- `$1
5971
- ${selectImport}`
5972
- );
5973
- if (replaced !== fixed) {
5974
- fixed = replaced;
5975
- } else {
5976
- fixed = selectImport + "\n" + fixed;
5977
- }
5978
- }
5979
- }
5980
- const lucideImportMatch = fixed.match(/import\s*\{([^}]+)\}\s*from\s*["']lucide-react["']/);
5981
- if (lucideImportMatch) {
5982
- let lucideExports = null;
5983
- try {
5984
- const { createRequire } = await import("module");
5985
- const require2 = createRequire(process.cwd() + "/package.json");
5986
- const lr = require2("lucide-react");
5987
- lucideExports = new Set(Object.keys(lr).filter((k) => /^[A-Z]/.test(k)));
5988
- } catch {
5989
- }
5990
- if (lucideExports) {
5991
- const nonLucideImports = /* @__PURE__ */ new Set();
5992
- for (const m of fixed.matchAll(/import\s*\{([^}]+)\}\s*from\s*["'](?!lucide-react)([^"']+)["']/g)) {
5993
- m[1].split(",").map((s) => s.trim()).filter(Boolean).forEach((n) => nonLucideImports.add(n));
5994
- }
5995
- const iconNames = lucideImportMatch[1].split(",").map((s) => s.trim()).filter(Boolean);
5996
- const duplicates = iconNames.filter((name) => nonLucideImports.has(name));
5997
- let newImport = lucideImportMatch[1];
5998
- for (const dup of duplicates) {
5999
- newImport = newImport.replace(new RegExp(`\\b${dup}\\b,?\\s*`), "");
6000
- fixes.push(`removed ${dup} from lucide import (conflicts with UI component import)`);
6001
- }
6002
- const invalid = iconNames.filter((name) => !lucideExports.has(name) && !nonLucideImports.has(name));
6003
- if (invalid.length > 0) {
6004
- const fallback = "Circle";
6005
- for (const bad of invalid) {
6006
- const re = new RegExp(`\\b${bad}\\b`, "g");
6007
- newImport = newImport.replace(re, fallback);
6008
- fixed = fixed.replace(re, fallback);
6009
- }
6010
- fixes.push(`invalid lucide icons \u2192 ${fallback}: ${invalid.join(", ")}`);
6011
- }
6012
- if (duplicates.length > 0 || invalid.length > 0) {
6013
- const importedNames = [
6014
- ...new Set(
6015
- newImport.split(",").map((s) => s.trim()).filter(Boolean)
6016
- )
6017
- ];
6018
- const originalImportLine = lucideImportMatch[0];
6019
- fixed = fixed.replace(originalImportLine, `import { ${importedNames.join(", ")} } from "lucide-react"`);
6020
- }
6021
- }
6022
- }
6023
- const lucideImportMatch2 = fixed.match(/import\s*\{([^}]+)\}\s*from\s*["']lucide-react["']/);
6024
- if (lucideImportMatch2) {
6025
- let lucideExports2 = null;
6026
- try {
6027
- const { createRequire } = await import("module");
6028
- const req = createRequire(process.cwd() + "/package.json");
6029
- const lr = req("lucide-react");
6030
- lucideExports2 = new Set(Object.keys(lr).filter((k) => /^[A-Z]/.test(k)));
6031
- } catch {
6032
- }
6033
- if (lucideExports2) {
6034
- const allImportedNames = /* @__PURE__ */ new Set();
6035
- for (const m of fixed.matchAll(/import\s*\{([^}]+)\}\s*from/g)) {
6036
- m[1].split(",").map((s) => s.trim()).filter(Boolean).forEach((n) => allImportedNames.add(n));
6037
- }
6038
- for (const m of fixed.matchAll(/import\s+([A-Z]\w+)\s+from/g)) {
6039
- allImportedNames.add(m[1]);
6040
- }
6041
- const lucideImported = new Set(
6042
- lucideImportMatch2[1].split(",").map((s) => s.trim()).filter(Boolean)
6043
- );
6044
- const jsxIconRefs = [...new Set([...fixed.matchAll(/<([A-Z][a-zA-Z]*Icon)\s/g)].map((m) => m[1]))];
6045
- const missing = [];
6046
- for (const ref of jsxIconRefs) {
6047
- if (allImportedNames.has(ref)) continue;
6048
- if (fixed.includes(`function ${ref}`) || fixed.includes(`const ${ref}`)) continue;
6049
- const baseName = ref.replace(/Icon$/, "");
6050
- if (lucideExports2.has(ref)) {
6051
- missing.push(ref);
6052
- lucideImported.add(ref);
6053
- } else if (lucideExports2.has(baseName)) {
6054
- const re = new RegExp(`\\b${ref}\\b`, "g");
6055
- fixed = fixed.replace(re, baseName);
6056
- missing.push(baseName);
6057
- lucideImported.add(baseName);
6058
- fixes.push(`renamed ${ref} \u2192 ${baseName} (lucide-react)`);
6059
- } else {
6060
- const fallback = "Circle";
6061
- const re = new RegExp(`\\b${ref}\\b`, "g");
6062
- fixed = fixed.replace(re, fallback);
6063
- lucideImported.add(fallback);
6064
- fixes.push(`unknown icon ${ref} \u2192 ${fallback}`);
6065
- }
6066
- }
6067
- if (missing.length > 0) {
6068
- const allNames = [...lucideImported];
6069
- const origLine = lucideImportMatch2[0];
6070
- fixed = fixed.replace(origLine, `import { ${allNames.join(", ")} } from "lucide-react"`);
6071
- fixes.push(`added missing lucide imports: ${missing.join(", ")}`);
6072
- }
6073
- }
6074
- }
6075
- const lucideNamesMatch = fixed.match(/import\s*\{([^}]+)\}\s*from\s*["']lucide-react["']/);
6076
- if (lucideNamesMatch) {
6077
- const lucideNames = new Set(
6078
- lucideNamesMatch[1].split(",").map((s) => s.trim()).filter(Boolean)
6079
- );
6080
- const beforeShrinkFix = fixed;
6081
- for (const iconName of lucideNames) {
6082
- const iconRe = new RegExp(`(<${iconName}\\s[^>]*className=")([^"]*)(")`, "g");
6083
- fixed = fixed.replace(iconRe, (_m, pre, classes, post) => {
6084
- if (/\bshrink-0\b/.test(classes)) return _m;
6085
- return `${pre}${classes} shrink-0${post}`;
6086
- });
6087
- }
6088
- if (fixed !== beforeShrinkFix) {
6089
- fixes.push("added shrink-0 to icons");
6090
- }
6091
- }
6092
- const linkWithButtonRe = /(<Link\b[^>]*>)\s*(<Button\b(?![^>]*asChild)[^>]*>)([\s\S]*?)<\/Button>\s*<\/Link>/g;
6093
- const beforeLinkFix = fixed;
6094
- fixed = fixed.replace(linkWithButtonRe, (_match, linkOpen, buttonOpen, inner) => {
6095
- const hrefMatch = linkOpen.match(/href="([^"]*)"/);
6096
- const href = hrefMatch ? hrefMatch[1] : "/";
6097
- const buttonWithAsChild = buttonOpen.replace("<Button", "<Button asChild");
6098
- return `${buttonWithAsChild}<Link href="${href}">${inner.trim()}</Link></Button>`;
6099
- });
6100
- if (fixed !== beforeLinkFix) {
6101
- fixes.push("Link>Button \u2192 Button asChild>Link (DOM nesting fix)");
6102
- }
6103
- const beforeAsChildFlex = fixed;
6104
- fixed = fixed.replace(
6105
- /(<Button\b[^>]*\basChild\b[^>]*>)\s*(<(?:Link|a)\b)([^>]*)(>)/g,
6106
- (_match, btnOpen, childTag, childProps, close) => {
6107
- if (/\binline-flex\b/.test(childProps)) return _match;
6108
- if (/className="([^"]*)"/.test(childProps)) {
6109
- const merged = childProps.replace(
6110
- /className="([^"]*)"/,
6111
- (_cm, classes) => `className="inline-flex items-center gap-2 ${classes}"`
6112
- );
6113
- return `${btnOpen}${childTag}${merged}${close}`;
6114
- }
6115
- return `${btnOpen}${childTag} className="inline-flex items-center gap-2"${close}`;
6116
- }
6117
- );
6118
- if (fixed !== beforeAsChildFlex) {
6119
- fixes.push("added inline-flex to Button asChild children (base-ui compat)");
6120
- }
6121
- const beforeLinkHrefFix = fixed;
6122
- fixed = fixed.replace(/<(Link|a)\b(?![^>]*\bhref\s*=)([^>]*)>([\s\S]*?)<\/\1>/g, (_match, tag, attrs, children) => {
6123
- const textContent = children.replace(/<[^>]*>/g, "").trim();
6124
- const href = resolveHref(textContent, context);
6125
- return `<${tag} href="${href}"${attrs}>${children}</${tag}>`;
6126
- });
6127
- fixed = fixed.replace(/<(Link|a)\b(?![^>]*\bhref\s*=)([^>]*)\/?>/g, '<$1 href="/"$2>');
6128
- if (fixed !== beforeLinkHrefFix) {
6129
- fixes.push("added href to <Link>/<a> missing href");
6130
- }
6131
- const { code: fixedByRules, fixes: ruleFixes } = applyComponentRules(fixed);
6132
- if (ruleFixes.length > 0) {
6133
- fixed = fixedByRules;
6134
- fixes.push(...ruleFixes);
6135
- }
6136
- const beforeTabsFix = fixed;
6137
- fixed = fixed.replace(
6138
- /(<TabsTrigger\b[^>]*className=")([^"]*)(")/g,
6139
- (_m, pre, classes, post) => {
6140
- const cleaned = classes.replace(/\b(border-input|border\b|outline\b)\s*/g, "").trim();
6141
- if (cleaned !== classes.trim()) return `${pre}${cleaned}${post}`;
6142
- return _m;
6143
- }
6144
- );
6145
- if (fixed !== beforeTabsFix) {
6146
- fixes.push("stripped border from TabsTrigger (shadcn handles active state)");
6147
- }
6148
- const beforeJunkFix = fixed;
6149
- fixed = fixed.replace(/className="([^"]*)"/g, (_match, classes) => {
6150
- const cleaned = classes.split(/\s+/).filter((c) => c !== "-0").join(" ");
6151
- if (cleaned !== classes.trim()) return `className="${cleaned}"`;
6152
- return _match;
6153
- });
6154
- if (fixed !== beforeJunkFix) {
6155
- fixes.push("removed junk classes (-0)");
6156
- }
6157
- fixed = fixed.replace(/className="([^"]*)"/g, (_match, inner) => {
6158
- const cleaned = inner.replace(/\s{2,}/g, " ").trim();
6159
- return `className="${cleaned}"`;
6160
- });
6161
- let imgCounter = 1;
6162
- const beforeImgFix = fixed;
6163
- fixed = fixed.replace(/["']\/api\/placeholder\/(\d+)\/(\d+)["']/g, (_m, w, h) => {
6164
- return `"https://picsum.photos/${w}/${h}?random=${imgCounter++}"`;
6165
- });
6166
- fixed = fixed.replace(/["']\/placeholder-avatar[^"']*["']/g, () => {
6167
- return `"https://i.pravatar.cc/150?u=user${imgCounter++}"`;
6168
- });
6169
- fixed = fixed.replace(/["']https?:\/\/via\.placeholder\.com\/(\d+)x?(\d*)(?:\/[^"']*)?\/?["']/g, (_m, w, h) => {
6170
- const height = h || w;
6171
- return `"https://picsum.photos/${w}/${height}?random=${imgCounter++}"`;
6172
- });
6173
- fixed = fixed.replace(/["']\/images\/[^"']+\.(?:jpg|jpeg|png|webp|gif)["']/g, () => {
6174
- return `"https://picsum.photos/800/400?random=${imgCounter++}"`;
6175
- });
6176
- fixed = fixed.replace(/["']\/placeholder[^"']*\.(?:jpg|jpeg|png|webp)["']/g, () => {
6177
- return `"https://picsum.photos/800/400?random=${imgCounter++}"`;
6178
- });
6179
- if (fixed !== beforeImgFix) {
6180
- fixes.push("placeholder images \u2192 working URLs (picsum/pravatar)");
6181
- }
6182
- return { code: fixed, fixes };
6183
- }
6184
- function formatIssues(issues) {
6185
- if (issues.length === 0) return "";
6186
- const errors = issues.filter((i) => i.severity === "error");
6187
- const warnings = issues.filter((i) => i.severity === "warning");
6188
- const infos = issues.filter((i) => i.severity === "info");
6189
- const lines = [];
6190
- if (errors.length > 0) {
6191
- lines.push(` \u274C ${errors.length} error(s):`);
6192
- for (const e of errors) {
6193
- lines.push(` L${e.line}: [${e.type}] ${e.message}`);
6194
- }
6195
- }
6196
- if (warnings.length > 0) {
6197
- lines.push(` \u26A0\uFE0F ${warnings.length} warning(s):`);
6198
- for (const w of warnings) {
6199
- lines.push(` L${w.line}: [${w.type}] ${w.message}`);
6200
- }
6201
- }
6202
- if (infos.length > 0) {
6203
- lines.push(` \u2139\uFE0F ${infos.length} info:`);
6204
- for (const i of infos) {
6205
- lines.push(` L${i.line}: [${i.type}] ${i.message}`);
6206
- }
6207
- }
6208
- return lines.join("\n");
6209
- }
6210
- function checkDesignConsistency(code) {
6211
- const warnings = [];
6212
- const hexPattern = /\[#[0-9a-fA-F]{3,8}\]/g;
6213
- for (const match of code.matchAll(hexPattern)) {
6214
- warnings.push({
6215
- type: "hardcoded-color",
6216
- message: `Hardcoded color ${match[0]} \u2014 use a design token (e.g., bg-primary) instead`
6217
- });
6218
- }
6219
- const spacingPattern = /[pm][trblxy]?-\[\d+px\]/g;
6220
- for (const match of code.matchAll(spacingPattern)) {
6221
- warnings.push({
6222
- type: "arbitrary-spacing",
6223
- message: `Arbitrary spacing ${match[0]} \u2014 use Tailwind spacing scale instead`
6224
- });
6225
- }
6226
- return warnings;
6227
- }
6228
- function verifyIncrementalEdit(before, after) {
6229
- const issues = [];
6230
- const hookPattern = /\buse[A-Z]\w+\s*\(/;
6231
- if (hookPattern.test(after) && !after.includes("'use client'") && !after.includes('"use client"')) {
6232
- issues.push({
6233
- type: "missing-use-client",
6234
- message: 'Code uses React hooks but missing "use client" directive'
6235
- });
6236
- }
6237
- if (!after.includes("export default")) {
6238
- issues.push({
6239
- type: "missing-default-export",
6240
- message: "Missing default export \u2014 page component must have a default export"
6241
- });
6242
- }
6243
- const importRegex = /import\s+\{([^}]+)\}\s+from/g;
6244
- const beforeImports = /* @__PURE__ */ new Set();
6245
- const afterImports = /* @__PURE__ */ new Set();
6246
- for (const match of before.matchAll(importRegex)) {
6247
- match[1].split(",").forEach((s) => beforeImports.add(s.trim()));
6248
- }
6249
- for (const match of after.matchAll(importRegex)) {
6250
- match[1].split(",").forEach((s) => afterImports.add(s.trim()));
6251
- }
6252
- for (const symbol of beforeImports) {
6253
- if (!afterImports.has(symbol) && symbol.length > 0) {
6254
- const codeWithoutImports = after.replace(/^import\s+.*$/gm, "");
6255
- const symbolRegex = new RegExp(`\\b${symbol}\\b`);
6256
- if (symbolRegex.test(codeWithoutImports)) {
6257
- issues.push({
6258
- type: "missing-import",
6259
- symbol,
6260
- message: `Import for "${symbol}" was removed but symbol is still used in code`
6261
- });
6262
- }
6263
- }
6264
- }
6265
- return issues;
6266
- }
6267
-
6268
4958
  // src/commands/chat/utils.ts
6269
4959
  import { resolve as resolve5 } from "path";
6270
4960
  import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
6271
4961
  import { DesignSystemManager as DesignSystemManager3, loadManifest as loadManifest4 } from "@getcoherent/core";
6272
- import chalk8 from "chalk";
4962
+ import chalk7 from "chalk";
6273
4963
  var MARKETING_ROUTES = /* @__PURE__ */ new Set(["", "landing", "pricing", "about", "contact", "blog", "features"]);
6274
4964
  var MIN_ANCHOR_PAGE_CODE_CHARS = 120;
6275
4965
  var AUTH_ROUTE_SLUGS = /* @__PURE__ */ new Set([
@@ -6370,7 +5060,7 @@ async function warnInlineDuplicates(projectRoot, pageName, route, pageCode, mani
6370
5060
  if (hasImport) continue;
6371
5061
  if (plannedForPage) {
6372
5062
  console.log(
6373
- chalk8.yellow(
5063
+ chalk7.yellow(
6374
5064
  `
6375
5065
  \u26A0 Page "${pageName}" should use shared component ${e.name} (per architecture plan) but it's not imported. Import from @/components/shared/${kebab}`
6376
5066
  )
@@ -6380,7 +5070,7 @@ async function warnInlineDuplicates(projectRoot, pageName, route, pageCode, mani
6380
5070
  const sameNameAsTag = new RegExp(`<\\/?${e.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s>]`).test(pageCode);
6381
5071
  if (sameNameAsTag) {
6382
5072
  console.log(
6383
- chalk8.yellow(
5073
+ chalk7.yellow(
6384
5074
  `
6385
5075
  \u26A0 Page "${pageName}" contains inline code similar to ${e.id} (${e.name}). Consider using the shared component instead.`
6386
5076
  )
@@ -6399,7 +5089,7 @@ async function warnInlineDuplicates(projectRoot, pageName, route, pageCode, mani
6399
5089
  const overlapRatio = sharedTokens.size > 0 ? overlap / sharedTokens.size : 0;
6400
5090
  if (overlap >= 20 && overlapRatio >= 0.6) {
6401
5091
  console.log(
6402
- chalk8.yellow(
5092
+ chalk7.yellow(
6403
5093
  `
6404
5094
  \u26A0 Page "${pageName}" contains inline code similar to ${e.id} (${e.name}). Consider using the shared component instead.`
6405
5095
  )
@@ -6449,10 +5139,10 @@ ${currentCode}
6449
5139
  \`\`\`` : "";
6450
5140
  return `Modify the shared component ${entry.name} (${entry.id}, file: ${entry.file}): ${message}. Read the current code below and apply the requested changes. Return the full updated component code as pageCode.${codeSnippet}`;
6451
5141
  }
6452
- console.log(chalk8.yellow(`
5142
+ console.log(chalk7.yellow(`
6453
5143
  \u26A0\uFE0F Component "${target}" not found in shared components.`));
6454
- console.log(chalk8.dim(" Available: " + manifest.shared.map((s) => `${s.id} ${s.name}`).join(", ")));
6455
- console.log(chalk8.dim(" Proceeding with message as-is...\n"));
5144
+ console.log(chalk7.dim(" Available: " + manifest.shared.map((s) => `${s.id} ${s.name}`).join(", ")));
5145
+ console.log(chalk7.dim(" Proceeding with message as-is...\n"));
6456
5146
  }
6457
5147
  if (options.page) {
6458
5148
  const target = options.page;
@@ -6474,10 +5164,10 @@ ${currentCode}
6474
5164
  \`\`\`` : "";
6475
5165
  return `Update page "${page.name}" (id: ${page.id}, route: ${page.route}, file: ${relPath}): ${message}. Read the current code below and apply the requested changes.${codeSnippet}`;
6476
5166
  }
6477
- console.log(chalk8.yellow(`
5167
+ console.log(chalk7.yellow(`
6478
5168
  \u26A0\uFE0F Page "${target}" not found.`));
6479
- console.log(chalk8.dim(" Available: " + config2.pages.map((p) => `${p.id} (${p.route})`).join(", ")));
6480
- console.log(chalk8.dim(" Proceeding with message as-is...\n"));
5169
+ console.log(chalk7.dim(" Available: " + config2.pages.map((p) => `${p.id} (${p.route})`).join(", ")));
5170
+ console.log(chalk7.dim(" Proceeding with message as-is...\n"));
6481
5171
  }
6482
5172
  if (options.token) {
6483
5173
  const target = options.token;
@@ -6997,7 +5687,7 @@ async function pMap(items, fn, concurrency = 3) {
6997
5687
  }
6998
5688
 
6999
5689
  // src/commands/chat/split-generator.ts
7000
- import chalk9 from "chalk";
5690
+ import chalk8 from "chalk";
7001
5691
  function buildExistingPagesContext(config2) {
7002
5692
  const pages = config2.pages || [];
7003
5693
  const analyzed = pages.filter((p) => p.pageAnalysis);
@@ -7094,6 +5784,43 @@ Before implementing any section, check this list. Import and use matching compon
7094
5784
 
7095
5785
  ${sharedComponentsSummary}`;
7096
5786
  }
5787
+ var RELEVANT_TYPES = {
5788
+ app: ["data-display", "form", "navigation", "feedback"],
5789
+ auth: ["form", "feedback"],
5790
+ marketing: ["section", "layout"]
5791
+ };
5792
+ function buildTieredComponentsPrompt(manifest, pageType) {
5793
+ if (manifest.shared.length === 0) return void 0;
5794
+ const relevantTypes = new Set(RELEVANT_TYPES[pageType] || RELEVANT_TYPES.app);
5795
+ const level1Lines = manifest.shared.map((e) => {
5796
+ const desc = e.description ? ` \u2014 ${e.description}` : "";
5797
+ return `- ${e.id} ${e.name} (${e.type})${desc}`;
5798
+ });
5799
+ const relevantComponents = manifest.shared.filter((e) => relevantTypes.has(e.type));
5800
+ const level2Blocks = relevantComponents.filter((e) => e.propsInterface || e.usageExample).map((e) => {
5801
+ const importPath = e.file.replace(/^components\/shared\//, "").replace(/\.tsx$/, "");
5802
+ const lines = [`### ${e.name} (${e.id})`];
5803
+ if (e.propsInterface) lines.push(`Props: ${e.propsInterface}`);
5804
+ if (e.usageExample) lines.push(`Usage: ${e.usageExample}`);
5805
+ lines.push(`Import: import { ${e.name} } from '@/components/shared/${importPath}'`);
5806
+ return lines.join("\n");
5807
+ });
5808
+ const sections = [
5809
+ `SHARED COMPONENTS \u2014 MANDATORY REUSE:`,
5810
+ `Before implementing any section, check this list. Import and use matching components. Do NOT re-implement these patterns inline.`,
5811
+ ``,
5812
+ `Available components:`,
5813
+ ...level1Lines
5814
+ ];
5815
+ if (level2Blocks.length > 0) {
5816
+ sections.push(``, `Components to use on this page (detailed API):`, ...level2Blocks);
5817
+ }
5818
+ sections.push(
5819
+ ``,
5820
+ `If you need a component from the list above that isn't detailed below, import it by path \u2014 the system will validate usage post-generation.`
5821
+ );
5822
+ return sections.join("\n");
5823
+ }
7097
5824
  function formatPlanSummary(plan) {
7098
5825
  if (plan.groups.length === 0) return "";
7099
5826
  const groupLines = plan.groups.map((g) => ` Group "${g.id}" (layout: ${g.layout}): ${g.pages.join(", ")}`);
@@ -7229,9 +5956,9 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
7229
5956
  const sharedSummary = plan.sharedComponents.length > 0 ? plan.sharedComponents.map((c) => `${c.name} \u2192 ${c.usedBy.join(", ")}`).join(" | ") : "";
7230
5957
  const totalPages = plan.groups.reduce((sum, g) => sum + g.pages.length, 0);
7231
5958
  spinner.succeed(`Phase 2/6 \u2014 Architecture plan created`);
7232
- console.log(chalk9.dim(` Groups: ${groupsSummary}`));
7233
- if (sharedSummary) console.log(chalk9.dim(` Shared: ${sharedSummary}`));
7234
- console.log(chalk9.dim(` Total: ${totalPages} pages, ${plan.sharedComponents.length} shared components`));
5959
+ console.log(chalk8.dim(` Groups: ${groupsSummary}`));
5960
+ if (sharedSummary) console.log(chalk8.dim(` Shared: ${sharedSummary}`));
5961
+ console.log(chalk8.dim(` Total: ${totalPages} pages, ${plan.sharedComponents.length} shared components`));
7235
5962
  if (plan.sharedComponents.length > 0 && parseOpts.projectRoot) {
7236
5963
  const allDeps = new Set(plan.sharedComponents.flatMap((c) => c.shadcnDeps));
7237
5964
  if (allDeps.size > 0) {
@@ -7248,7 +5975,7 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
7248
5975
  spinner.warn("Phase 2/6 \u2014 Plan generation failed (continuing without plan)");
7249
5976
  }
7250
5977
  for (const w of planWarnings) {
7251
- console.log(chalk9.dim(` ${w}`));
5978
+ console.log(chalk8.dim(` ${w}`));
7252
5979
  }
7253
5980
  } catch {
7254
5981
  spinner.warn("Phase 2/6 \u2014 Plan generation failed (continuing without plan)");
@@ -7309,7 +6036,7 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
7309
6036
  if (plan && plan.sharedComponents.length > 0) {
7310
6037
  spinner.start(`Phase 4.5/6 \u2014 Generating ${plan.sharedComponents.length} shared components from plan...`);
7311
6038
  try {
7312
- const { generateSharedComponentsFromPlan } = await import("./plan-generator-QUESV7GS.js");
6039
+ const { generateSharedComponentsFromPlan } = await import("./plan-generator-H55WEIY2.js");
7313
6040
  const generated = await generateSharedComponentsFromPlan(
7314
6041
  plan,
7315
6042
  styleContext,
@@ -7353,6 +6080,7 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
7353
6080
  spinner.start(`Phase 5/6 \u2014 Generating ${remainingPages.length} pages in parallel...`);
7354
6081
  const sharedLayoutNote = "Header and Footer are shared components rendered by the root layout. Do NOT include any site-wide <header>, <nav>, or <footer> in this page. Start with the main content directly.";
7355
6082
  const sharedComponentsNote = buildSharedComponentsNote(parseOpts.sharedComponentsSummary);
6083
+ const currentManifest = projectRoot ? await loadManifest5(projectRoot) : null;
7356
6084
  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="#".`;
7357
6085
  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.';
7358
6086
  const planSummaryNote = plan ? formatPlanSummary(plan) : "";
@@ -7373,6 +6101,7 @@ ${existingAppPageCode}
7373
6101
  const pageType = plan ? getPageType(route, plan) : inferPageTypeFromRoute(route);
7374
6102
  const designConstraints = getDesignQualityForType(pageType);
7375
6103
  const authNote = isAuth ? 'For this auth page: the auth layout already provides centering (flex items-center justify-center min-h-svh). Do NOT add your own centering wrapper or min-h-svh. Just output a div with className="w-full max-w-md" containing the Card. Do NOT use section containers or full-width wrappers.' : void 0;
6104
+ const tieredNote = currentManifest ? buildTieredComponentsPrompt(currentManifest, pageType) : void 0;
7376
6105
  const prompt = [
7377
6106
  `Create ONE page called "${name}" at route "${route}".`,
7378
6107
  `Context: ${message}.`,
@@ -7380,7 +6109,7 @@ ${existingAppPageCode}
7380
6109
  `PAGE TYPE: ${pageType}`,
7381
6110
  designConstraints,
7382
6111
  sharedLayoutNote,
7383
- sharedComponentsNote,
6112
+ tieredNote || sharedComponentsNote,
7384
6113
  routeNote,
7385
6114
  alignmentNote,
7386
6115
  authNote,
@@ -7413,12 +6142,14 @@ ${existingAppPageCode}
7413
6142
  const pageRoute = page.route || `/${pageName.toLowerCase()}`;
7414
6143
  try {
7415
6144
  const retryPageType = plan ? getPageType(pageRoute, plan) : inferPageTypeFromRoute(pageRoute);
6145
+ const retryTieredNote = currentManifest ? buildTieredComponentsPrompt(currentManifest, retryPageType) : void 0;
7416
6146
  const lightweightPrompt = buildLightweightPagePrompt(
7417
6147
  pageName,
7418
6148
  pageRoute,
7419
6149
  styleContext || "",
7420
6150
  parseOpts.sharedComponentsSummary,
7421
- retryPageType
6151
+ retryPageType,
6152
+ retryTieredNote
7422
6153
  );
7423
6154
  const retryResult = await parseModification(lightweightPrompt, modCtx, provider, {
7424
6155
  ...parseOpts,
@@ -7549,7 +6280,7 @@ function extractAppNameFromPrompt(prompt) {
7549
6280
  import { resolve as resolve8 } from "path";
7550
6281
  import { mkdir as mkdir4 } from "fs/promises";
7551
6282
  import { dirname as dirname6 } from "path";
7552
- import chalk12 from "chalk";
6283
+ import chalk11 from "chalk";
7553
6284
  import {
7554
6285
  getTemplateForPageType,
7555
6286
  loadManifest as loadManifest6,
@@ -7570,7 +6301,7 @@ import {
7570
6301
  TailwindConfigGenerator
7571
6302
  } from "@getcoherent/core";
7572
6303
  import { integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout2, generateSharedComponent as generateSharedComponent3 } from "@getcoherent/core";
7573
- import chalk10 from "chalk";
6304
+ import chalk9 from "chalk";
7574
6305
 
7575
6306
  // src/utils/file-hashes.ts
7576
6307
  import { createHash } from "crypto";
@@ -7674,7 +6405,7 @@ async function canOverwriteShared(projectRoot, componentFile, storedHashes) {
7674
6405
  if (!storedHash) return true;
7675
6406
  const edited = await isManuallyEdited(filePath, storedHash);
7676
6407
  if (edited) {
7677
- console.log(chalk10.yellow(` \u26A0 Skipping ${componentFile} \u2014 manually edited since last generation`));
6408
+ console.log(chalk9.yellow(` \u26A0 Skipping ${componentFile} \u2014 manually edited since last generation`));
7678
6409
  }
7679
6410
  return !edited;
7680
6411
  }
@@ -7737,7 +6468,7 @@ async function regenerateLayout(config2, projectRoot, options = { navChanged: fa
7737
6468
  await ensureAppRouteGroupLayout(projectRoot, config2.navigation?.type, options.navChanged);
7738
6469
  } catch (err) {
7739
6470
  if (process.env.COHERENT_DEBUG === "1") {
7740
- console.log(chalk10.dim("Layout integration warning:", err));
6471
+ console.log(chalk9.dim("Layout integration warning:", err));
7741
6472
  }
7742
6473
  }
7743
6474
  }
@@ -7879,7 +6610,7 @@ async function regenerateFiles(modified, config2, projectRoot, options = { navCh
7879
6610
  });
7880
6611
  const sharedInstalled = await scanAndInstallSharedDeps(projectRoot);
7881
6612
  if (sharedInstalled.length > 0 && process.env.COHERENT_DEBUG === "1") {
7882
- console.log(chalk10.dim(` Auto-installed shared deps: ${sharedInstalled.join(", ")}`));
6613
+ console.log(chalk9.dim(` Auto-installed shared deps: ${sharedInstalled.join(", ")}`));
7883
6614
  }
7884
6615
  }
7885
6616
  if (componentIds.size > 0) {
@@ -7936,7 +6667,7 @@ function extractBalancedTag(source, tagName) {
7936
6667
  }
7937
6668
 
7938
6669
  // src/commands/chat/reporting.ts
7939
- import chalk11 from "chalk";
6670
+ import chalk10 from "chalk";
7940
6671
  function extractImportsFrom(code, fromPath) {
7941
6672
  const escaped = fromPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7942
6673
  const regex = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*['"\`]${escaped}[^'"\`]*['"\`]`, "g");
@@ -7958,27 +6689,27 @@ function printPostGenerationReport(opts) {
7958
6689
  const iconCount = extractImportsFrom(code, "lucide-react").length;
7959
6690
  const hasInstalled = postFixes.some((f) => f.startsWith("Installed:"));
7960
6691
  const syntaxStatus = postFixes.length > 0 ? postFixes.some((f) => f.includes("metadata")) ? "fixed (escaped metadata quotes) \u2714" : "fixed \u2714" : "valid \u2714";
7961
- console.log(chalk11.green(`
6692
+ console.log(chalk10.green(`
7962
6693
  \u2705 Page "${pageTitle}" ${action} at ${filePath}
7963
6694
  `));
7964
6695
  if (uiComponents.length > 0) {
7965
- console.log(chalk11.dim(` Components: ${uiComponents.join(", ")} (from @/components/ui)`));
6696
+ console.log(chalk10.dim(` Components: ${uiComponents.join(", ")} (from @/components/ui)`));
7966
6697
  }
7967
6698
  if (inCodeShared.length > 0) {
7968
- console.log(chalk11.dim(` Shared: ${inCodeShared.map((s) => `${s.id} (${s.name})`).join(", ")}`));
6699
+ console.log(chalk10.dim(` Shared: ${inCodeShared.map((s) => `${s.id} (${s.name})`).join(", ")}`));
7969
6700
  }
7970
6701
  if (layoutShared.length > 0) {
7971
- console.log(chalk11.dim(` Layout: ${layoutShared.map((l) => `${l.id} (${l.name})`).join(", ")} via layout.tsx`));
6702
+ console.log(chalk10.dim(` Layout: ${layoutShared.map((l) => `${l.id} (${l.name})`).join(", ")} via layout.tsx`));
7972
6703
  }
7973
6704
  if (iconCount > 0) {
7974
- console.log(chalk11.dim(` Icons: ${iconCount} from lucide-react`));
6705
+ console.log(chalk10.dim(` Icons: ${iconCount} from lucide-react`));
7975
6706
  }
7976
6707
  if (hasInstalled) {
7977
- console.log(chalk11.dim(" Dependencies: installed \u2714"));
6708
+ console.log(chalk10.dim(" Dependencies: installed \u2714"));
7978
6709
  }
7979
- console.log(chalk11.dim(` Syntax: ${syntaxStatus}`));
6710
+ console.log(chalk10.dim(` Syntax: ${syntaxStatus}`));
7980
6711
  if (route) {
7981
- console.log(chalk11.cyan(`
6712
+ console.log(chalk10.cyan(`
7982
6713
  Preview: http://localhost:3000${route}`));
7983
6714
  }
7984
6715
  console.log("");
@@ -7986,35 +6717,35 @@ function printPostGenerationReport(opts) {
7986
6717
  function printSharedComponentReport(opts) {
7987
6718
  const { id, name, file, instruction, postFixes = [] } = opts;
7988
6719
  const syntaxStatus = postFixes.length > 0 ? "fixed \u2714" : "valid \u2714";
7989
- console.log(chalk11.green(`
6720
+ console.log(chalk10.green(`
7990
6721
  \u2705 Updated ${id} (${name}) at ${file}
7991
6722
  `));
7992
6723
  if (instruction) {
7993
6724
  const snippet = instruction.length > 60 ? instruction.slice(0, 57) + "..." : instruction;
7994
- console.log(chalk11.dim(` Changed: ${snippet}`));
6725
+ console.log(chalk10.dim(` Changed: ${snippet}`));
7995
6726
  }
7996
- console.log(chalk11.dim(" Affects: all pages via layout.tsx"));
7997
- console.log(chalk11.dim(` Syntax: ${syntaxStatus}`));
6727
+ console.log(chalk10.dim(" Affects: all pages via layout.tsx"));
6728
+ console.log(chalk10.dim(` Syntax: ${syntaxStatus}`));
7998
6729
  console.log("");
7999
6730
  }
8000
6731
  function printLinkSharedReport(opts) {
8001
6732
  const { sharedId, sharedName, pageTarget, route, postFixes = [] } = opts;
8002
6733
  const syntaxStatus = postFixes.length > 0 ? "fixed \u2714" : "valid \u2714";
8003
- console.log(chalk11.green(`
6734
+ console.log(chalk10.green(`
8004
6735
  \u2705 Linked ${sharedId} (${sharedName}) to page "${pageTarget}"
8005
6736
  `));
8006
- console.log(chalk11.dim(` Syntax: ${syntaxStatus}`));
8007
- console.log(chalk11.cyan(` Preview: http://localhost:3000${route}`));
6737
+ console.log(chalk10.dim(` Syntax: ${syntaxStatus}`));
6738
+ console.log(chalk10.cyan(` Preview: http://localhost:3000${route}`));
8008
6739
  console.log("");
8009
6740
  }
8010
6741
  function printPromoteAndLinkReport(opts) {
8011
6742
  const { id, name, file, usedInFiles, postFixes = [] } = opts;
8012
6743
  const syntaxStatus = postFixes.length > 0 ? "fixed \u2714" : "valid \u2714";
8013
- console.log(chalk11.green(`
6744
+ console.log(chalk10.green(`
8014
6745
  \u2705 Created ${id} (${name}) at ${file}
8015
6746
  `));
8016
- console.log(chalk11.dim(` Linked to: ${usedInFiles.length} page(s)`));
8017
- console.log(chalk11.dim(` Syntax: ${syntaxStatus}`));
6747
+ console.log(chalk10.dim(` Linked to: ${usedInFiles.length} page(s)`));
6748
+ console.log(chalk10.dim(` Syntax: ${syntaxStatus}`));
8018
6749
  console.log("");
8019
6750
  }
8020
6751
  function showPreview(requests, results, config2, preflightInstalledNames) {
@@ -8032,23 +6763,23 @@ function showPreview(requests, results, config2, preflightInstalledNames) {
8032
6763
  const modifiedSharedComponents = successfulPairs.filter(({ request }) => request.type === "modify-layout-block");
8033
6764
  const modifiedPages = successfulPairs.filter(({ request }) => request.type === "update-page");
8034
6765
  const tokenChanges = successfulPairs.filter(({ request }) => request.type === "update-token");
8035
- console.log(chalk11.bold.cyan("\n\u{1F4CB} Changes Applied:\n"));
6766
+ console.log(chalk10.bold.cyan("\n\u{1F4CB} Changes Applied:\n"));
8036
6767
  if (preflightInstalledNames && preflightInstalledNames.length > 0) {
8037
- console.log(chalk11.cyan("\u{1F50D} Pre-flight check: Installed missing components:"));
6768
+ console.log(chalk10.cyan("\u{1F50D} Pre-flight check: Installed missing components:"));
8038
6769
  preflightInstalledNames.forEach((name) => {
8039
- console.log(chalk11.green(` \u2728 Auto-installed ${name}`));
6770
+ console.log(chalk10.green(` \u2728 Auto-installed ${name}`));
8040
6771
  });
8041
6772
  console.log("");
8042
6773
  }
8043
6774
  if (addedComponents.length > 0) {
8044
6775
  const names = addedComponents.map(({ request }) => request.changes.name).filter(Boolean);
8045
- console.log(chalk11.green("\u{1F4E6} Components:"));
8046
- console.log(chalk11.white(` \u2728 Auto-installed: ${names.join(", ")}`));
6776
+ console.log(chalk10.green("\u{1F4E6} Components:"));
6777
+ console.log(chalk10.white(` \u2728 Auto-installed: ${names.join(", ")}`));
8047
6778
  }
8048
6779
  if (customComponents.length > 0) {
8049
6780
  const names = customComponents.map(({ request }) => request.changes.name).filter(Boolean);
8050
- if (addedComponents.length === 0) console.log(chalk11.green("\u{1F4E6} Components:"));
8051
- console.log(chalk11.white(` \u2728 Created: ${names.join(", ")}`));
6781
+ if (addedComponents.length === 0) console.log(chalk10.green("\u{1F4E6} Components:"));
6782
+ console.log(chalk10.white(` \u2728 Created: ${names.join(", ")}`));
8052
6783
  }
8053
6784
  const usedComponentIds = /* @__PURE__ */ new Set();
8054
6785
  addedPages.forEach(({ request }) => {
@@ -8063,73 +6794,73 @@ function showPreview(requests, results, config2, preflightInstalledNames) {
8063
6794
  ]);
8064
6795
  const reusedIds = [...usedComponentIds].filter((id) => !newComponentIds.has(id));
8065
6796
  if (reusedIds.length > 0) {
8066
- if (addedComponents.length === 0 && customComponents.length === 0) console.log(chalk11.green("\u{1F4E6} Components:"));
8067
- console.log(chalk11.white(` \u{1F504} Reused: ${reusedIds.join(", ")}`));
6797
+ if (addedComponents.length === 0 && customComponents.length === 0) console.log(chalk10.green("\u{1F4E6} Components:"));
6798
+ console.log(chalk10.white(` \u{1F504} Reused: ${reusedIds.join(", ")}`));
8068
6799
  }
8069
6800
  if (addedComponents.length > 0 || customComponents.length > 0 || reusedIds.length > 0) {
8070
6801
  console.log("");
8071
6802
  }
8072
6803
  if (addedPages.length > 0) {
8073
- console.log(chalk11.green("\u{1F4C4} Pages Created:"));
6804
+ console.log(chalk10.green("\u{1F4C4} Pages Created:"));
8074
6805
  addedPages.forEach(({ request }) => {
8075
6806
  const page = request.changes;
8076
6807
  const route = page.route || "/";
8077
- console.log(chalk11.white(` \u2728 ${page.name || "Page"}`));
8078
- console.log(chalk11.gray(` Route: ${route}`));
6808
+ console.log(chalk10.white(` \u2728 ${page.name || "Page"}`));
6809
+ console.log(chalk10.gray(` Route: ${route}`));
8079
6810
  const configPage = config2.pages?.find((p) => p.id === page.id || p.route === (page.route || "/"));
8080
6811
  const sectionCount = configPage?.pageAnalysis?.sections?.length ?? page.sections?.length ?? 0;
8081
- console.log(chalk11.gray(` Sections: ${sectionCount}`));
6812
+ console.log(chalk10.gray(` Sections: ${sectionCount}`));
8082
6813
  });
8083
6814
  console.log("");
8084
6815
  }
8085
6816
  if (modifiedComponents.length > 0 || modifiedSharedComponents.length > 0 || modifiedPages.length > 0 || tokenChanges.length > 0) {
8086
- console.log(chalk11.yellow("\u{1F527} Modified:"));
6817
+ console.log(chalk10.yellow("\u{1F527} Modified:"));
8087
6818
  modifiedComponents.forEach(({ result }) => {
8088
- console.log(chalk11.white(` \u2022 ${result.message}`));
6819
+ console.log(chalk10.white(` \u2022 ${result.message}`));
8089
6820
  });
8090
6821
  modifiedSharedComponents.forEach(({ result }) => {
8091
- console.log(chalk11.white(` \u2022 ${result.message}`));
6822
+ console.log(chalk10.white(` \u2022 ${result.message}`));
8092
6823
  });
8093
6824
  modifiedPages.forEach(({ result }) => {
8094
- console.log(chalk11.white(` \u2022 ${result.message}`));
6825
+ console.log(chalk10.white(` \u2022 ${result.message}`));
8095
6826
  });
8096
6827
  tokenChanges.forEach(({ result }) => {
8097
- console.log(chalk11.white(` \u2022 ${result.message}`));
6828
+ console.log(chalk10.white(` \u2022 ${result.message}`));
8098
6829
  });
8099
6830
  console.log("");
8100
6831
  }
8101
6832
  if (failedPairs.length > 0) {
8102
- console.log(chalk11.red("\u274C Failed modifications:"));
6833
+ console.log(chalk10.red("\u274C Failed modifications:"));
8103
6834
  failedPairs.forEach(({ result }) => {
8104
- console.log(chalk11.gray(` \u2716 ${result.message}`));
6835
+ console.log(chalk10.gray(` \u2716 ${result.message}`));
8105
6836
  });
8106
6837
  console.log("");
8107
6838
  }
8108
6839
  const successCount = successfulPairs.length;
8109
6840
  const totalCount = results.length;
8110
6841
  if (successCount === totalCount) {
8111
- console.log(chalk11.green.bold(`\u2705 Success! ${successCount} modification(s) applied
6842
+ console.log(chalk10.green.bold(`\u2705 Success! ${successCount} modification(s) applied
8112
6843
  `));
8113
6844
  } else {
8114
- console.log(chalk11.yellow.bold(`\u26A0\uFE0F Partial success: ${successCount}/${totalCount} modification(s) applied
6845
+ console.log(chalk10.yellow.bold(`\u26A0\uFE0F Partial success: ${successCount}/${totalCount} modification(s) applied
8115
6846
  `));
8116
6847
  }
8117
6848
  if (addedPages.length > 0) {
8118
6849
  const firstPage = addedPages[0].request.changes;
8119
6850
  const route = firstPage?.route || "/";
8120
- console.log(chalk11.cyan("\u{1F680} What's next:\n"));
8121
- console.log(chalk11.white(" \u{1F4FA} View in browser:"));
8122
- console.log(chalk11.cyan(" coherent preview"));
8123
- console.log(chalk11.gray(` \u2192 Opens http://localhost:3000${route}
6851
+ console.log(chalk10.cyan("\u{1F680} What's next:\n"));
6852
+ console.log(chalk10.white(" \u{1F4FA} View in browser:"));
6853
+ console.log(chalk10.cyan(" coherent preview"));
6854
+ console.log(chalk10.gray(` \u2192 Opens http://localhost:3000${route}
8124
6855
  `));
8125
- console.log(chalk11.white(" \u{1F3A8} Customize:"));
8126
- console.log(chalk11.cyan(' coherent chat "make buttons rounded"'));
8127
- console.log(chalk11.cyan(` coherent chat "add hero section to ${firstPage?.name ?? "page"}"`));
6856
+ console.log(chalk10.white(" \u{1F3A8} Customize:"));
6857
+ console.log(chalk10.cyan(' coherent chat "make buttons rounded"'));
6858
+ console.log(chalk10.cyan(` coherent chat "add hero section to ${firstPage?.name ?? "page"}"`));
8128
6859
  console.log("");
8129
6860
  } else if (successCount > 0) {
8130
- console.log(chalk11.cyan("\u{1F680} What's next:\n"));
8131
- console.log(chalk11.white(" \u{1F4FA} Preview changes:"));
8132
- console.log(chalk11.cyan(" coherent preview\n"));
6861
+ console.log(chalk10.cyan("\u{1F680} What's next:\n"));
6862
+ console.log(chalk10.white(" \u{1F4FA} Preview changes:"));
6863
+ console.log(chalk10.cyan(" coherent preview\n"));
8133
6864
  }
8134
6865
  }
8135
6866
  function getChangeDescription(request, config2) {
@@ -8278,8 +7009,8 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
8278
7009
  const newCode = await ai.editSharedComponentCode(currentCode, instruction, resolved.name);
8279
7010
  const { fixedCode, fixes } = await validateAndFixGeneratedCode(projectRoot, newCode, { isPage: false });
8280
7011
  if (fixes.length > 0) {
8281
- console.log(chalk12.dim(" \u{1F527} Post-generation fixes:"));
8282
- fixes.forEach((f) => console.log(chalk12.dim(` ${f}`)));
7012
+ console.log(chalk11.dim(" \u{1F527} Post-generation fixes:"));
7013
+ fixes.forEach((f) => console.log(chalk11.dim(` ${f}`)));
8283
7014
  }
8284
7015
  await writeFile(fullPath, fixedCode);
8285
7016
  printSharedComponentReport({
@@ -8352,8 +7083,8 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
8352
7083
  const newPageCode = await ai.replaceInlineWithShared(pageCode, sharedCode, resolved.name, changes?.blockHint);
8353
7084
  const { fixedCode, fixes } = await validateAndFixGeneratedCode(projectRoot, newPageCode, { isPage: true });
8354
7085
  if (fixes.length > 0) {
8355
- console.log(chalk12.dim(" \u{1F527} Post-generation fixes:"));
8356
- fixes.forEach((f) => console.log(chalk12.dim(` ${f}`)));
7086
+ console.log(chalk11.dim(" \u{1F527} Post-generation fixes:"));
7087
+ fixes.forEach((f) => console.log(chalk11.dim(` ${f}`)));
8357
7088
  }
8358
7089
  await writeFile(pageFilePath, fixedCode);
8359
7090
  const manifest = await loadManifest6(projectRoot);
@@ -8455,8 +7186,8 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
8455
7186
  const newCode = await ai.replaceInlineWithShared(linkPageCode, sharedCode, created.name, blockHint);
8456
7187
  const { fixedCode, fixes } = await validateAndFixGeneratedCode(projectRoot, newCode, { isPage: true });
8457
7188
  if (fixes.length > 0) {
8458
- console.log(chalk12.dim(" \u{1F527} Post-generation fixes:"));
8459
- fixes.forEach((f) => console.log(chalk12.dim(` ${f}`)));
7189
+ console.log(chalk11.dim(" \u{1F527} Post-generation fixes:"));
7190
+ fixes.forEach((f) => console.log(chalk11.dim(` ${f}`)));
8460
7191
  }
8461
7192
  await writeFile(fullPath, fixedCode);
8462
7193
  usedInFiles.push(relPath);
@@ -8547,7 +7278,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
8547
7278
  const aiPageCode = typeof page.pageCode === "string" && page.pageCode.trim() !== "" ? page.pageCode : void 0;
8548
7279
  if (aiPageCode) {
8549
7280
  finalPageCode = aiPageCode;
8550
- if (DEBUG2) console.log(chalk12.dim(` [pageCode] Using AI-generated pageCode (user content priority)`));
7281
+ if (DEBUG2) console.log(chalk11.dim(` [pageCode] Using AI-generated pageCode (user content priority)`));
8551
7282
  } else {
8552
7283
  const inferredType = page.pageType || inferPageType(page.route || "", page.name || "");
8553
7284
  if (inferredType) {
@@ -8562,19 +7293,19 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
8562
7293
  const content = page.structuredContent || getDefaultContent(inferredType, page.name || pageName);
8563
7294
  finalPageCode = templateFn(content, opts);
8564
7295
  if (DEBUG2)
8565
- console.log(chalk12.dim(` [template] Used "${inferredType}" template (inferred from route/name)`));
7296
+ console.log(chalk11.dim(` [template] Used "${inferredType}" template (inferred from route/name)`));
8566
7297
  } catch {
8567
- if (DEBUG2) console.log(chalk12.dim(` [template] Failed for "${inferredType}"`));
7298
+ if (DEBUG2) console.log(chalk11.dim(` [template] Failed for "${inferredType}"`));
8568
7299
  }
8569
7300
  }
8570
7301
  }
8571
7302
  }
8572
7303
  if (!finalPageCode) {
8573
- console.log(chalk12.yellow(`
7304
+ console.log(chalk11.yellow(`
8574
7305
  \u26A0\uFE0F Page "${page.name || page.id}" has no generated code \u2014 it will appear empty.`));
8575
- console.log(chalk12.dim(" This usually means the AI did not produce pageCode for this page."));
7306
+ console.log(chalk11.dim(" This usually means the AI did not produce pageCode for this page."));
8576
7307
  console.log(
8577
- chalk12.dim(
7308
+ chalk11.dim(
8578
7309
  ' Try running: coherent chat "regenerate the ' + (page.name || page.id) + ' page with full content"'
8579
7310
  )
8580
7311
  );
@@ -8646,8 +7377,8 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
8646
7377
  const allFixes = [...postFixes, ...autoFixes];
8647
7378
  if (stripped.length > 0) allFixes.push(`stripped inline ${stripped.join(", ")} (layout owns these)`);
8648
7379
  if (allFixes.length > 0) {
8649
- console.log(chalk12.dim(" \u{1F527} Post-generation fixes:"));
8650
- allFixes.forEach((f) => console.log(chalk12.dim(` ${f}`)));
7380
+ console.log(chalk11.dim(" \u{1F527} Post-generation fixes:"));
7381
+ allFixes.forEach((f) => console.log(chalk11.dim(` ${f}`)));
8651
7382
  }
8652
7383
  await writeFile(filePath, codeToWrite);
8653
7384
  const pageIdx = dsm.getConfig().pages.findIndex((p) => p.id === page.id);
@@ -8684,7 +7415,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
8684
7415
  const errors = issues.filter((i) => i.severity === "error");
8685
7416
  if (errors.length >= 2 && aiProvider) {
8686
7417
  console.log(
8687
- chalk12.yellow(`
7418
+ chalk11.yellow(`
8688
7419
  \u{1F504} ${errors.length} quality errors \u2014 attempting AI fix for ${page.name || page.id}...`)
8689
7420
  );
8690
7421
  try {
@@ -8713,7 +7444,7 @@ Rules:
8713
7444
  await writeFile(filePath, codeToWrite);
8714
7445
  issues = validatePageQuality(codeToWrite, void 0, qualityPageType);
8715
7446
  const finalErrors = issues.filter((i) => i.severity === "error").length;
8716
- console.log(chalk12.green(` \u2714 Quality fix: ${errors.length} \u2192 ${finalErrors} errors`));
7447
+ console.log(chalk11.green(` \u2714 Quality fix: ${errors.length} \u2192 ${finalErrors} errors`));
8717
7448
  }
8718
7449
  }
8719
7450
  }
@@ -8722,15 +7453,15 @@ Rules:
8722
7453
  }
8723
7454
  const report = formatIssues(issues);
8724
7455
  if (report) {
8725
- console.log(chalk12.yellow(`
7456
+ console.log(chalk11.yellow(`
8726
7457
  \u{1F50D} Quality check for ${page.name || page.id}:`));
8727
- console.log(chalk12.dim(report));
7458
+ console.log(chalk11.dim(report));
8728
7459
  }
8729
7460
  const consistency = checkDesignConsistency(codeToWrite);
8730
7461
  if (consistency.length > 0) {
8731
- console.log(chalk12.yellow(`
7462
+ console.log(chalk11.yellow(`
8732
7463
  \u{1F3A8} Design consistency for ${page.name || page.id}:`));
8733
- consistency.forEach((w) => console.log(chalk12.dim(` \u26A0 [${w.type}] ${w.message}`)));
7464
+ consistency.forEach((w) => console.log(chalk11.dim(` \u26A0 [${w.type}] ${w.message}`)));
8734
7465
  }
8735
7466
  }
8736
7467
  }
@@ -8745,9 +7476,9 @@ Rules:
8745
7476
  const changes = request.changes;
8746
7477
  const instruction = originalMessage || (typeof changes?.instruction === "string" ? changes.instruction : void 0);
8747
7478
  let resolvedPageCode = typeof changes?.pageCode === "string" && changes.pageCode.trim() !== "" ? changes.pageCode : void 0;
8748
- if (DEBUG2 && instruction) console.log(chalk12.dim(` [update-page] instruction: ${instruction.slice(0, 120)}...`));
7479
+ if (DEBUG2 && instruction) console.log(chalk11.dim(` [update-page] instruction: ${instruction.slice(0, 120)}...`));
8749
7480
  if (DEBUG2 && resolvedPageCode)
8750
- console.log(chalk12.dim(` [update-page] has pageCode (${resolvedPageCode.length} chars)`));
7481
+ console.log(chalk11.dim(` [update-page] has pageCode (${resolvedPageCode.length} chars)`));
8751
7482
  const configChanges = { ...changes };
8752
7483
  delete configChanges.pageCode;
8753
7484
  delete configChanges.pageType;
@@ -8771,12 +7502,12 @@ Rules:
8771
7502
  try {
8772
7503
  currentCode = await readFile(absPath);
8773
7504
  } catch {
8774
- if (DEBUG2) console.log(chalk12.dim(` [update-page] Could not read current file at ${absPath}`));
7505
+ if (DEBUG2) console.log(chalk11.dim(` [update-page] Could not read current file at ${absPath}`));
8775
7506
  }
8776
7507
  if (currentCode) {
8777
7508
  const ai = await createAIProvider(aiProvider ?? "auto");
8778
7509
  if (ai.editPageCode) {
8779
- console.log(chalk12.dim(" \u270F\uFE0F Applying changes to existing page..."));
7510
+ console.log(chalk11.dim(" \u270F\uFE0F Applying changes to existing page..."));
8780
7511
  const coreRules = CORE_CONSTRAINTS;
8781
7512
  const pageRoute = pageDef.route || `/${pageDef.id}`;
8782
7513
  const pageType = inferPageTypeFromRoute(pageRoute);
@@ -8799,15 +7530,15 @@ ${contextualRules}
8799
7530
  ${routeRules}
8800
7531
  ${pagesCtx}`
8801
7532
  );
8802
- if (DEBUG2) console.log(chalk12.dim(` [update-page] AI returned ${resolvedPageCode.length} chars`));
7533
+ if (DEBUG2) console.log(chalk11.dim(` [update-page] AI returned ${resolvedPageCode.length} chars`));
8803
7534
  const editIssues = verifyIncrementalEdit(currentCode, resolvedPageCode);
8804
7535
  if (editIssues.length > 0) {
8805
- console.log(chalk12.yellow(`
7536
+ console.log(chalk11.yellow(`
8806
7537
  \u26A0 Incremental edit issues for ${pageDef.name || pageDef.id}:`));
8807
- editIssues.forEach((issue) => console.log(chalk12.dim(` [${issue.type}] ${issue.message}`)));
7538
+ editIssues.forEach((issue) => console.log(chalk11.dim(` [${issue.type}] ${issue.message}`)));
8808
7539
  }
8809
7540
  } else {
8810
- console.log(chalk12.yellow(" \u26A0 AI provider does not support editPageCode"));
7541
+ console.log(chalk11.yellow(" \u26A0 AI provider does not support editPageCode"));
8811
7542
  }
8812
7543
  }
8813
7544
  }
@@ -8865,8 +7596,8 @@ ${pagesCtx}`
8865
7596
  const allFixes = [...postFixes, ...autoFixes];
8866
7597
  if (stripped.length > 0) allFixes.push(`stripped inline ${stripped.join(", ")} (layout owns these)`);
8867
7598
  if (allFixes.length > 0) {
8868
- console.log(chalk12.dim(" \u{1F527} Post-generation fixes:"));
8869
- allFixes.forEach((f) => console.log(chalk12.dim(` ${f}`)));
7599
+ console.log(chalk11.dim(" \u{1F527} Post-generation fixes:"));
7600
+ allFixes.forEach((f) => console.log(chalk11.dim(` ${f}`)));
8870
7601
  }
8871
7602
  await writeFile(absPath, codeToWrite);
8872
7603
  const updatePageIdx = dsm.getConfig().pages.findIndex((p) => p.id === pageDef.id);
@@ -8902,15 +7633,15 @@ ${pagesCtx}`
8902
7633
  const issues = validatePageQuality(codeToWrite, void 0, qualityPageType2);
8903
7634
  const report = formatIssues(issues);
8904
7635
  if (report) {
8905
- console.log(chalk12.yellow(`
7636
+ console.log(chalk11.yellow(`
8906
7637
  \u{1F50D} Quality check for ${pageDef.name || pageDef.id}:`));
8907
- console.log(chalk12.dim(report));
7638
+ console.log(chalk11.dim(report));
8908
7639
  }
8909
7640
  const consistency = checkDesignConsistency(codeToWrite);
8910
7641
  if (consistency.length > 0) {
8911
- console.log(chalk12.yellow(`
7642
+ console.log(chalk11.yellow(`
8912
7643
  \u{1F3A8} Design consistency for ${pageDef.name || pageDef.id}:`));
8913
- consistency.forEach((w) => console.log(chalk12.dim(` \u26A0 [${w.type}] ${w.message}`)));
7644
+ consistency.forEach((w) => console.log(chalk11.dim(` \u26A0 [${w.type}] ${w.message}`)));
8914
7645
  }
8915
7646
  } else {
8916
7647
  try {
@@ -8925,8 +7656,8 @@ ${pagesCtx}`
8925
7656
  if (fixes.length > 0) {
8926
7657
  code = fixed;
8927
7658
  await writeFile(absPath, code);
8928
- console.log(chalk12.dim(" \u{1F527} Auto-fixes applied:"));
8929
- fixes.forEach((f) => console.log(chalk12.dim(` ${f}`)));
7659
+ console.log(chalk11.dim(" \u{1F527} Auto-fixes applied:"));
7660
+ fixes.forEach((f) => console.log(chalk11.dim(` ${f}`)));
8930
7661
  }
8931
7662
  const relFilePath = routeToRelPath(route, isAuth);
8932
7663
  const manifest = await loadManifest6(projectRoot);
@@ -8945,9 +7676,9 @@ ${pagesCtx}`
8945
7676
  const issues = validatePageQuality(code, void 0, pageTypeForQuality);
8946
7677
  const report = formatIssues(issues);
8947
7678
  if (report) {
8948
- console.log(chalk12.yellow(`
7679
+ console.log(chalk11.yellow(`
8949
7680
  \u{1F50D} Quality check for ${pageDef.name || pageDef.id}:`));
8950
- console.log(chalk12.dim(report));
7681
+ console.log(chalk11.dim(report));
8951
7682
  }
8952
7683
  } catch {
8953
7684
  }
@@ -9070,7 +7801,7 @@ function hasNavChanged(before, after) {
9070
7801
  }
9071
7802
 
9072
7803
  // src/commands/chat/interactive.ts
9073
- import chalk13 from "chalk";
7804
+ import chalk12 from "chalk";
9074
7805
  import { resolve as resolve9 } from "path";
9075
7806
  import { existsSync as existsSync16, readFileSync as readFileSync11, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5 } from "fs";
9076
7807
  import { DesignSystemManager as DesignSystemManager6, ComponentManager as ComponentManager4, loadManifest as loadManifest7 } from "@getcoherent/core";
@@ -9088,7 +7819,7 @@ async function interactiveChat(options, chatCommandFn) {
9088
7819
  const validProviders = ["claude", "openai", "auto"];
9089
7820
  const provider = (options.provider || "auto").toLowerCase();
9090
7821
  if (!validProviders.includes(provider)) {
9091
- console.error(chalk13.red(`
7822
+ console.error(chalk12.red(`
9092
7823
  \u274C Invalid provider: ${options.provider}`));
9093
7824
  process.exit(1);
9094
7825
  }
@@ -9103,13 +7834,13 @@ async function interactiveChat(options, chatCommandFn) {
9103
7834
  } catch (e) {
9104
7835
  if (DEBUG3) console.error("Failed to load REPL history:", e);
9105
7836
  }
9106
- console.log(chalk13.cyan("\n\u{1F3A8} Coherent Interactive Mode"));
9107
- console.log(chalk13.dim(" Type your requests, or use built-in commands."));
9108
- console.log(chalk13.dim(' Type "help" for available commands, "exit" to quit.\n'));
7837
+ console.log(chalk12.cyan("\n\u{1F3A8} Coherent Interactive Mode"));
7838
+ console.log(chalk12.dim(" Type your requests, or use built-in commands."));
7839
+ console.log(chalk12.dim(' Type "help" for available commands, "exit" to quit.\n'));
9109
7840
  const rl = createInterface({
9110
7841
  input: process.stdin,
9111
7842
  output: process.stdout,
9112
- prompt: chalk13.cyan("Coherent> "),
7843
+ prompt: chalk12.cyan("Coherent> "),
9113
7844
  history,
9114
7845
  historySize: 200
9115
7846
  });
@@ -9123,37 +7854,45 @@ async function interactiveChat(options, chatCommandFn) {
9123
7854
  const lower = input.toLowerCase();
9124
7855
  if (lower === "exit" || lower === "quit" || lower === "q") {
9125
7856
  saveHistory();
9126
- console.log(chalk13.dim("\nBye!\n"));
7857
+ console.log(chalk12.dim("\nBye!\n"));
9127
7858
  rl.close();
9128
7859
  process.exit(0);
9129
7860
  }
9130
7861
  if (lower === "help") {
9131
- console.log(chalk13.bold("\n Built-in commands:"));
9132
- console.log(chalk13.white(" components") + chalk13.dim(" \u2014 list shared and UI components"));
9133
- console.log(chalk13.white(" pages") + chalk13.dim(" \u2014 list all pages"));
9134
- console.log(chalk13.white(" tokens") + chalk13.dim(" \u2014 show design tokens"));
9135
- console.log(chalk13.white(" status") + chalk13.dim(" \u2014 project summary"));
9136
- console.log(chalk13.white(" help") + chalk13.dim(" \u2014 this help"));
9137
- console.log(chalk13.white(" exit") + chalk13.dim(" \u2014 quit interactive mode"));
9138
- console.log(chalk13.bold("\n Target shortcuts:"));
9139
- console.log(chalk13.white(" @ComponentName <msg>") + chalk13.dim(" \u2014 target a shared component"));
9140
- console.log(chalk13.white(" @/route <msg>") + chalk13.dim(" \u2014 target a page by route"));
9141
- console.log(chalk13.dim("\n Anything else is sent to AI as a modification request.\n"));
7862
+ console.log(chalk12.bold("\n Built-in commands:"));
7863
+ console.log(chalk12.white(" components") + chalk12.dim(" \u2014 list shared and UI components"));
7864
+ console.log(chalk12.white(" pages") + chalk12.dim(" \u2014 list all pages"));
7865
+ console.log(chalk12.white(" tokens") + chalk12.dim(" \u2014 show design tokens"));
7866
+ console.log(chalk12.white(" status") + chalk12.dim(" \u2014 project summary"));
7867
+ console.log(chalk12.white(" help") + chalk12.dim(" \u2014 this help"));
7868
+ console.log(chalk12.white(" exit") + chalk12.dim(" \u2014 quit interactive mode"));
7869
+ console.log(chalk12.bold("\n Target shortcuts:"));
7870
+ console.log(chalk12.white(" @ComponentName <msg>") + chalk12.dim(" \u2014 target a shared component"));
7871
+ console.log(chalk12.white(" @/route <msg>") + chalk12.dim(" \u2014 target a page by route"));
7872
+ console.log(chalk12.dim("\n Anything else is sent to AI as a modification request.\n"));
9142
7873
  rl.prompt();
9143
7874
  return;
9144
7875
  }
9145
7876
  if (lower === "components" || lower === "list components" || lower.includes("what components")) {
9146
7877
  const manifest = await loadManifest7(projectRoot);
9147
7878
  if (manifest.shared.length === 0) {
9148
- console.log(chalk13.gray("\n No shared components yet.\n"));
7879
+ console.log(chalk12.gray("\n No shared components yet.\n"));
9149
7880
  } else {
9150
7881
  console.log("");
9151
- const order = { layout: 0, section: 1, widget: 2 };
7882
+ const order = {
7883
+ layout: 0,
7884
+ navigation: 1,
7885
+ "data-display": 2,
7886
+ form: 3,
7887
+ feedback: 4,
7888
+ section: 5,
7889
+ widget: 6
7890
+ };
9152
7891
  const sorted = [...manifest.shared].sort((a, b) => (order[a.type] ?? 9) - (order[b.type] ?? 9));
9153
7892
  sorted.forEach((entry) => {
9154
- const usage = entry.usedIn.includes("app/layout.tsx") ? chalk13.green("all pages") : entry.usedIn.length > 0 ? chalk13.gray(entry.usedIn.join(", ")) : chalk13.gray("unused");
7893
+ const usage = entry.usedIn.includes("app/layout.tsx") ? chalk12.green("all pages") : entry.usedIn.length > 0 ? chalk12.gray(entry.usedIn.join(", ")) : chalk12.gray("unused");
9155
7894
  console.log(
9156
- ` ${chalk13.cyan(entry.id.padEnd(8))} ${chalk13.white(entry.name.padEnd(18))} ${chalk13.gray(entry.type.padEnd(9))} ${usage}`
7895
+ ` ${chalk12.cyan(entry.id.padEnd(8))} ${chalk12.white(entry.name.padEnd(18))} ${chalk12.gray(entry.type.padEnd(9))} ${usage}`
9157
7896
  );
9158
7897
  });
9159
7898
  console.log("");
@@ -9164,11 +7903,11 @@ async function interactiveChat(options, chatCommandFn) {
9164
7903
  if (lower === "pages" || lower === "list pages" || lower.includes("what pages")) {
9165
7904
  const currentConfig = dsm.getConfig();
9166
7905
  if (currentConfig.pages.length === 0) {
9167
- console.log(chalk13.gray("\n No pages yet.\n"));
7906
+ console.log(chalk12.gray("\n No pages yet.\n"));
9168
7907
  } else {
9169
7908
  console.log("");
9170
7909
  currentConfig.pages.forEach((p) => {
9171
- console.log(` ${chalk13.white(p.name.padEnd(22))} ${chalk13.gray(p.route)}`);
7910
+ console.log(` ${chalk12.white(p.name.padEnd(22))} ${chalk12.gray(p.route)}`);
9172
7911
  });
9173
7912
  console.log("");
9174
7913
  }
@@ -9178,10 +7917,10 @@ async function interactiveChat(options, chatCommandFn) {
9178
7917
  if (lower === "status") {
9179
7918
  const currentConfig = dsm.getConfig();
9180
7919
  const manifest = await loadManifest7(projectRoot);
9181
- console.log(chalk13.bold(`
7920
+ console.log(chalk12.bold(`
9182
7921
  ${currentConfig.name || "Coherent Project"}`));
9183
7922
  console.log(
9184
- chalk13.dim(
7923
+ chalk12.dim(
9185
7924
  ` Pages: ${currentConfig.pages.length} | Shared components: ${manifest.shared.length} | UI components: ${cm.getAllComponents().length}
9186
7925
  `
9187
7926
  )
@@ -9192,21 +7931,21 @@ async function interactiveChat(options, chatCommandFn) {
9192
7931
  if (lower === "tokens" || lower === "show tokens" || lower === "design tokens") {
9193
7932
  const currentConfig = dsm.getConfig();
9194
7933
  const t = currentConfig.tokens;
9195
- console.log(chalk13.bold("\n Design Tokens\n"));
9196
- console.log(chalk13.cyan(" Colors (light)"));
7934
+ console.log(chalk12.bold("\n Design Tokens\n"));
7935
+ console.log(chalk12.cyan(" Colors (light)"));
9197
7936
  for (const [k, v] of Object.entries(t.colors.light)) {
9198
- console.log(` ${chalk13.white(k.padEnd(14))} ${chalk13.gray(v)}`);
7937
+ console.log(` ${chalk12.white(k.padEnd(14))} ${chalk12.gray(v)}`);
9199
7938
  }
9200
- console.log(chalk13.cyan("\n Typography"));
9201
- console.log(` ${chalk13.white("sans".padEnd(14))} ${chalk13.gray(t.typography.fontFamily.sans)}`);
9202
- console.log(` ${chalk13.white("mono".padEnd(14))} ${chalk13.gray(t.typography.fontFamily.mono)}`);
9203
- console.log(chalk13.cyan("\n Spacing"));
7939
+ console.log(chalk12.cyan("\n Typography"));
7940
+ console.log(` ${chalk12.white("sans".padEnd(14))} ${chalk12.gray(t.typography.fontFamily.sans)}`);
7941
+ console.log(` ${chalk12.white("mono".padEnd(14))} ${chalk12.gray(t.typography.fontFamily.mono)}`);
7942
+ console.log(chalk12.cyan("\n Spacing"));
9204
7943
  for (const [k, v] of Object.entries(t.spacing)) {
9205
- console.log(` ${chalk13.white(k.padEnd(14))} ${chalk13.gray(v)}`);
7944
+ console.log(` ${chalk12.white(k.padEnd(14))} ${chalk12.gray(v)}`);
9206
7945
  }
9207
- console.log(chalk13.cyan("\n Radius"));
7946
+ console.log(chalk12.cyan("\n Radius"));
9208
7947
  for (const [k, v] of Object.entries(t.radius)) {
9209
- console.log(` ${chalk13.white(k.padEnd(14))} ${chalk13.gray(v)}`);
7948
+ console.log(` ${chalk12.white(k.padEnd(14))} ${chalk12.gray(v)}`);
9210
7949
  }
9211
7950
  console.log("");
9212
7951
  rl.prompt();
@@ -9228,7 +7967,7 @@ async function interactiveChat(options, chatCommandFn) {
9228
7967
  await dsm.load();
9229
7968
  } catch (err) {
9230
7969
  if (!err._printed) {
9231
- console.error(chalk13.red(`
7970
+ console.error(chalk12.red(`
9232
7971
  Error: ${err.message}
9233
7972
  `));
9234
7973
  }
@@ -9260,8 +7999,8 @@ async function chatCommand(message, options) {
9260
7999
  process.exit(1);
9261
8000
  };
9262
8001
  if (!message) {
9263
- console.error(chalk14.red('\n\u274C No message provided. Use: coherent chat "your request"\n'));
9264
- console.log(chalk14.dim(" Or use interactive mode: coherent chat -i\n"));
8002
+ console.error(chalk13.red('\n\u274C No message provided. Use: coherent chat "your request"\n'));
8003
+ console.log(chalk13.dim(" Or use interactive mode: coherent chat -i\n"));
9265
8004
  bail("No message provided");
9266
8005
  }
9267
8006
  const spinner = ora2("Processing your request...").start();
@@ -9271,7 +8010,7 @@ async function chatCommand(message, options) {
9271
8010
  const migrationGuard = join11(projectRoot, ".coherent", "migration-in-progress");
9272
8011
  if (existsSync17(migrationGuard)) {
9273
8012
  spinner.fail("Migration in progress");
9274
- console.error(chalk14.red("\n\u274C A migration is in progress. Run `coherent migrate --rollback` to undo first."));
8013
+ console.error(chalk13.red("\n\u274C A migration is in progress. Run `coherent migrate --rollback` to undo first."));
9275
8014
  bail("Migration in progress");
9276
8015
  }
9277
8016
  const validProviders = ["claude", "openai", "auto"];
@@ -9281,20 +8020,20 @@ async function chatCommand(message, options) {
9281
8020
  releaseLock = await acquireProjectLock(projectRoot);
9282
8021
  if (!validProviders.includes(provider)) {
9283
8022
  spinner.fail("Invalid provider");
9284
- console.error(chalk14.red(`
8023
+ console.error(chalk13.red(`
9285
8024
  \u274C Invalid provider: ${options.provider}`));
9286
- console.log(chalk14.dim(`Valid options: ${validProviders.join(", ")}`));
8025
+ console.log(chalk13.dim(`Valid options: ${validProviders.join(", ")}`));
9287
8026
  bail(`Invalid provider: ${options.provider}`);
9288
8027
  }
9289
8028
  spinner.text = "Loading design system configuration...";
9290
8029
  const config2 = await loadConfig(configPath);
9291
8030
  if (config2.coherentVersion && config2.coherentVersion !== CLI_VERSION2) {
9292
8031
  spinner.stop();
9293
- console.log(chalk14.yellow("\n\u26A0\uFE0F Version mismatch detected\n"));
9294
- console.log(chalk14.gray(" Project created with: ") + chalk14.white(`v${config2.coherentVersion}`));
9295
- console.log(chalk14.gray(" Current CLI version: ") + chalk14.white(`v${CLI_VERSION2}`));
9296
- console.log(chalk14.cyan("\n \u{1F4A1} Run `coherent update` to apply latest changes to your project.\n"));
9297
- console.log(chalk14.dim(" Continuing anyway...\n"));
8032
+ console.log(chalk13.yellow("\n\u26A0\uFE0F Version mismatch detected\n"));
8033
+ console.log(chalk13.gray(" Project created with: ") + chalk13.white(`v${config2.coherentVersion}`));
8034
+ console.log(chalk13.gray(" Current CLI version: ") + chalk13.white(`v${CLI_VERSION2}`));
8035
+ console.log(chalk13.cyan("\n \u{1F4A1} Run `coherent update` to apply latest changes to your project.\n"));
8036
+ console.log(chalk13.dim(" Continuing anyway...\n"));
9298
8037
  spinner.start("Loading design system configuration...");
9299
8038
  }
9300
8039
  if (needsGlobalsFix(projectRoot)) {
@@ -9314,6 +8053,64 @@ async function chatCommand(message, options) {
9314
8053
  const pm = new PageManager3(config2, cm);
9315
8054
  spinner.succeed("Configuration loaded");
9316
8055
  message = await resolveTargetFlags(message, options, config2, projectRoot);
8056
+ if (options.newComponent) {
8057
+ const componentName = options.newComponent;
8058
+ spinner.start(`Creating shared component: ${componentName}...`);
8059
+ const { createAIProvider: createAIProvider2 } = await import("./ai-provider-HUQO64P3.js");
8060
+ const { generateSharedComponent: generateSharedComponent7 } = await import("@getcoherent/core");
8061
+ const { autoFixCode: autoFixCode2 } = await import("./quality-validator-3K5BMJSR.js");
8062
+ const { extractPropsInterface, extractDependencies } = await import("./component-extractor-VYJLT5NR.js");
8063
+ const aiProvider = await createAIProvider2(provider ?? "auto");
8064
+ const prompt = `Generate a React component called "${componentName}". Description: ${message}.
8065
+ Use shadcn/ui components and Tailwind CSS semantic tokens. Export the component as a named export.
8066
+ Include a TypeScript props interface.
8067
+ Return JSON: { "requests": [{ "type": "add-page", "changes": { "name": "${componentName}", "pageCode": "...full TSX..." } }] }`;
8068
+ const raw = await aiProvider.parseModification(prompt);
8069
+ const requests2 = Array.isArray(raw) ? raw : raw?.requests ?? [];
8070
+ const codeMatch = requests2.find(
8071
+ (r) => r.changes?.pageCode
8072
+ );
8073
+ const rawCode = codeMatch?.changes?.pageCode || "";
8074
+ if (!rawCode) {
8075
+ spinner.fail(`Could not generate component ${componentName}`);
8076
+ releaseLock?.();
8077
+ return;
8078
+ }
8079
+ const { code: fixedCode } = await autoFixCode2(rawCode);
8080
+ const props = extractPropsInterface(fixedCode);
8081
+ const deps = extractDependencies(fixedCode);
8082
+ const componentType = options.type || "section";
8083
+ const genResult = await generateSharedComponent7(projectRoot, {
8084
+ name: componentName,
8085
+ type: componentType,
8086
+ code: fixedCode,
8087
+ description: message,
8088
+ propsInterface: props ?? void 0,
8089
+ dependencies: deps,
8090
+ source: "manual"
8091
+ });
8092
+ if (!options.type) {
8093
+ try {
8094
+ const { classifyComponents } = await import("./ai-classifier-EGXPFJLN.js");
8095
+ const classifications = await classifyComponents(
8096
+ [{ name: componentName, signature: props || componentName }],
8097
+ async (p) => JSON.stringify(await aiProvider.generateJSON("You are a component classifier.", p))
8098
+ );
8099
+ if (classifications.length > 0) {
8100
+ let manifest2 = await loadManifest8(projectRoot);
8101
+ manifest2 = updateEntry(manifest2, genResult.id, {
8102
+ type: classifications[0].type,
8103
+ description: classifications[0].description || message
8104
+ });
8105
+ await saveManifest2(projectRoot, manifest2);
8106
+ }
8107
+ } catch {
8108
+ }
8109
+ }
8110
+ spinner.succeed(`Created ${genResult.name} (${genResult.id}) at ${genResult.file}`);
8111
+ releaseLock?.();
8112
+ return;
8113
+ }
9317
8114
  const isPageGenRequest = /\bpages?\s*[:)]/i.test(message) || /\bcreate\b.*\bpage/i.test(message) || message.length > 200;
9318
8115
  if (!isPageGenRequest) {
9319
8116
  if (/switch to dark mode|default to dark|make.*dark.*(default|theme)|dark theme/i.test(message)) {
@@ -9321,9 +8118,9 @@ async function chatCommand(message, options) {
9321
8118
  const done = await setDefaultDarkTheme(projectRoot);
9322
8119
  spinner.stop();
9323
8120
  if (done) {
9324
- console.log(chalk14.green("\n\u2705 Default theme set to dark. Reload the app to see changes.\n"));
8121
+ console.log(chalk13.green("\n\u2705 Default theme set to dark. Reload the app to see changes.\n"));
9325
8122
  } else {
9326
- console.log(chalk14.yellow("\n\u26A0\uFE0F Could not update layout (app/layout.tsx not found).\n"));
8123
+ console.log(chalk13.yellow("\n\u26A0\uFE0F Could not update layout (app/layout.tsx not found).\n"));
9327
8124
  }
9328
8125
  return;
9329
8126
  }
@@ -9339,10 +8136,10 @@ async function chatCommand(message, options) {
9339
8136
  dsm.updateConfig(cfg);
9340
8137
  dsm.save();
9341
8138
  spinner.stop();
9342
- console.log(chalk14.green("\n\u2705 Default theme set to light. Reload the app to see changes.\n"));
8139
+ console.log(chalk13.green("\n\u2705 Default theme set to light. Reload the app to see changes.\n"));
9343
8140
  } catch {
9344
8141
  spinner.stop();
9345
- console.log(chalk14.yellow("\n\u26A0\uFE0F Could not update layout (app/layout.tsx not found).\n"));
8142
+ console.log(chalk13.yellow("\n\u26A0\uFE0F Could not update layout (app/layout.tsx not found).\n"));
9346
8143
  }
9347
8144
  return;
9348
8145
  }
@@ -9352,7 +8149,7 @@ async function chatCommand(message, options) {
9352
8149
  const { created, id } = await ensureThemeToggle(projectRoot);
9353
8150
  spinner.stop();
9354
8151
  console.log(
9355
- chalk14.green(
8152
+ chalk13.green(
9356
8153
  `
9357
8154
  \u2705 ${created ? `Created ${id} (ThemeToggle) and added to layout` : "ThemeToggle already present; layout updated"}.
9358
8155
  `
@@ -9360,7 +8157,7 @@ async function chatCommand(message, options) {
9360
8157
  );
9361
8158
  } catch (e) {
9362
8159
  spinner.fail("Failed to add theme toggle");
9363
- if (e instanceof Error) console.error(chalk14.red("\n\u274C " + e.message + "\n"));
8160
+ if (e instanceof Error) console.error(chalk13.red("\n\u274C " + e.message + "\n"));
9364
8161
  }
9365
8162
  return;
9366
8163
  }
@@ -9376,12 +8173,12 @@ async function chatCommand(message, options) {
9376
8173
  manifest = { ...manifest, shared: validShared };
9377
8174
  await saveManifest2(project.root, manifest);
9378
8175
  if (DEBUG4) {
9379
- console.log(chalk14.dim(`[pre-gen] Cleaned ${cleaned} orphaned component(s) from manifest`));
8176
+ console.log(chalk13.dim(`[pre-gen] Cleaned ${cleaned} orphaned component(s) from manifest`));
9380
8177
  }
9381
8178
  }
9382
8179
  const sharedComponentsSummary = buildSharedComponentsSummary(manifest);
9383
8180
  if (DEBUG4 && sharedComponentsSummary) {
9384
- console.log(chalk14.dim("[add-page] sharedComponentsSummary in prompt:\n" + sharedComponentsSummary));
8181
+ console.log(chalk13.dim("[add-page] sharedComponentsSummary in prompt:\n" + sharedComponentsSummary));
9385
8182
  }
9386
8183
  let requests;
9387
8184
  let uxRecommendations;
@@ -9488,10 +8285,10 @@ async function chatCommand(message, options) {
9488
8285
  }
9489
8286
  if (requests.length === 0) {
9490
8287
  spinner.fail("No modifications found in your request");
9491
- console.log(chalk14.yellow("\n\u{1F4A1} Try being more specific, e.g.:"));
9492
- console.log(chalk14.dim(' - "make buttons blue"'));
9493
- console.log(chalk14.dim(' - "add a pricing page"'));
9494
- console.log(chalk14.dim(' - "change primary color to green"'));
8288
+ console.log(chalk13.yellow("\n\u{1F4A1} Try being more specific, e.g.:"));
8289
+ console.log(chalk13.dim(' - "make buttons blue"'));
8290
+ console.log(chalk13.dim(' - "add a pricing page"'));
8291
+ console.log(chalk13.dim(' - "change primary color to green"'));
9495
8292
  return;
9496
8293
  }
9497
8294
  spinner.succeed(`Parsed ${requests.length} modification(s)`);
@@ -9499,11 +8296,11 @@ async function chatCommand(message, options) {
9499
8296
  normalizedRequests = normalizedRequests.map((req) => {
9500
8297
  const result = normalizeRequest(req, dsm.getConfig());
9501
8298
  if ("error" in result) {
9502
- console.log(chalk14.yellow(` \u26A0 Skipped: ${result.error}`));
8299
+ console.log(chalk13.yellow(` \u26A0 Skipped: ${result.error}`));
9503
8300
  return null;
9504
8301
  }
9505
8302
  if (result.type !== req.type) {
9506
- console.log(chalk14.dim(` \u2139 Adjusted: ${req.type} \u2192 ${result.type} (target: ${req.target})`));
8303
+ console.log(chalk13.dim(` \u2139 Adjusted: ${req.type} \u2192 ${result.type} (target: ${req.target})`));
9507
8304
  }
9508
8305
  return result;
9509
8306
  }).filter((r) => r !== null);
@@ -9559,13 +8356,13 @@ async function chatCommand(message, options) {
9559
8356
  }
9560
8357
  }
9561
8358
  if (DEBUG4) {
9562
- console.log(chalk14.gray(`
8359
+ console.log(chalk13.gray(`
9563
8360
  [DEBUG] Pre-flight analysis for page "${page.name || page.route}": `));
9564
- console.log(chalk14.gray(` Page sections: ${page.sections?.length || 0}`));
8361
+ console.log(chalk13.gray(` Page sections: ${page.sections?.length || 0}`));
9565
8362
  if (page.sections?.[0]?.props?.fields) {
9566
- console.log(chalk14.gray(` First section has ${page.sections[0].props.fields.length} fields`));
8363
+ console.log(chalk13.gray(` First section has ${page.sections[0].props.fields.length} fields`));
9567
8364
  page.sections[0].props.fields.forEach((f, i) => {
9568
- console.log(chalk14.gray(` Field ${i}: component=${f.component}`));
8365
+ console.log(chalk13.gray(` Field ${i}: component=${f.component}`));
9569
8366
  });
9570
8367
  }
9571
8368
  }
@@ -9588,8 +8385,8 @@ async function chatCommand(message, options) {
9588
8385
  const INVALID_COMPONENT_IDS = /* @__PURE__ */ new Set(["ui", "shared", "lib", "utils", "hooks", "app", "components"]);
9589
8386
  for (const id of INVALID_COMPONENT_IDS) allNeededComponentIds.delete(id);
9590
8387
  if (DEBUG4) {
9591
- console.log(chalk14.gray("\n[DEBUG] Pre-flight analysis (consolidated):"));
9592
- console.log(chalk14.gray(` All needed components: ${Array.from(allNeededComponentIds).join(", ")}`));
8388
+ console.log(chalk13.gray("\n[DEBUG] Pre-flight analysis (consolidated):"));
8389
+ console.log(chalk13.gray(` All needed components: ${Array.from(allNeededComponentIds).join(", ")}`));
9593
8390
  console.log("");
9594
8391
  }
9595
8392
  const missingComponents = [];
@@ -9597,59 +8394,59 @@ async function chatCommand(message, options) {
9597
8394
  const isRegistered = !!cm.read(componentId);
9598
8395
  const filePath = join11(projectRoot, "components", "ui", `${componentId}.tsx`);
9599
8396
  const fileExists = existsSync17(filePath);
9600
- if (DEBUG4) console.log(chalk14.gray(` Checking ${componentId}: registered=${isRegistered} file=${fileExists}`));
8397
+ if (DEBUG4) console.log(chalk13.gray(` Checking ${componentId}: registered=${isRegistered} file=${fileExists}`));
9601
8398
  if (!isRegistered || !fileExists) {
9602
8399
  missingComponents.push(componentId);
9603
8400
  }
9604
8401
  }
9605
8402
  if (missingComponents.length > 0) {
9606
8403
  spinner.stop();
9607
- console.log(chalk14.cyan("\n\u{1F50D} Pre-flight check: Installing missing components...\n"));
8404
+ console.log(chalk13.cyan("\n\u{1F50D} Pre-flight check: Installing missing components...\n"));
9608
8405
  const provider2 = getComponentProvider();
9609
8406
  for (const componentId of missingComponents) {
9610
8407
  if (DEBUG4) {
9611
- console.log(chalk14.gray(` [DEBUG] Trying to install: ${componentId}`));
9612
- console.log(chalk14.gray(` [DEBUG] provider.has(${componentId}): ${provider2.has(componentId)}`));
8408
+ console.log(chalk13.gray(` [DEBUG] Trying to install: ${componentId}`));
8409
+ console.log(chalk13.gray(` [DEBUG] provider.has(${componentId}): ${provider2.has(componentId)}`));
9613
8410
  }
9614
8411
  if (provider2.has(componentId)) {
9615
8412
  try {
9616
8413
  const result = await provider2.installComponent(componentId, projectRoot);
9617
- if (DEBUG4) console.log(chalk14.gray(` [DEBUG] installComponent result: ${result.success}`));
8414
+ if (DEBUG4) console.log(chalk13.gray(` [DEBUG] installComponent result: ${result.success}`));
9618
8415
  if (result.success && result.componentDef) {
9619
8416
  if (!cm.read(componentId)) {
9620
8417
  if (DEBUG4)
9621
8418
  console.log(
9622
- chalk14.gray(` [DEBUG] Registering ${result.componentDef.id} (${result.componentDef.name})`)
8419
+ chalk13.gray(` [DEBUG] Registering ${result.componentDef.id} (${result.componentDef.name})`)
9623
8420
  );
9624
8421
  const regResult = await cm.register(result.componentDef);
9625
8422
  if (DEBUG4) {
9626
8423
  console.log(
9627
- chalk14.gray(
8424
+ chalk13.gray(
9628
8425
  ` [DEBUG] Register result: ${regResult.success ? "SUCCESS" : "FAILED"}${!regResult.success && regResult.message ? ` - ${regResult.message}` : ""}`
9629
8426
  )
9630
8427
  );
9631
8428
  }
9632
8429
  if (regResult.success) {
9633
8430
  preflightInstalledIds.push(result.componentDef.id);
9634
- console.log(chalk14.green(` \u2728 Auto-installed ${result.componentDef.name} component`));
8431
+ console.log(chalk13.green(` \u2728 Auto-installed ${result.componentDef.name} component`));
9635
8432
  dsm.updateConfig(regResult.config);
9636
8433
  cm.updateConfig(regResult.config);
9637
8434
  pm.updateConfig(regResult.config);
9638
8435
  }
9639
8436
  } else {
9640
8437
  preflightInstalledIds.push(result.componentDef.id);
9641
- console.log(chalk14.green(` \u2728 Re-installed ${result.componentDef.name} component (file was missing)`));
8438
+ console.log(chalk13.green(` \u2728 Re-installed ${result.componentDef.name} component (file was missing)`));
9642
8439
  }
9643
8440
  }
9644
8441
  } catch (error) {
9645
- console.log(chalk14.red(` \u274C Failed to install ${componentId}:`));
9646
- console.log(chalk14.red(` ${error instanceof Error ? error.message : error}`));
8442
+ console.log(chalk13.red(` \u274C Failed to install ${componentId}:`));
8443
+ console.log(chalk13.red(` ${error instanceof Error ? error.message : error}`));
9647
8444
  if (error instanceof Error && error.stack) {
9648
- console.log(chalk14.gray(` ${error.stack.split("\n")[1]}`));
8445
+ console.log(chalk13.gray(` ${error.stack.split("\n")[1]}`));
9649
8446
  }
9650
8447
  }
9651
8448
  } else {
9652
- console.log(chalk14.yellow(` \u26A0\uFE0F Component ${componentId} not available`));
8449
+ console.log(chalk13.yellow(` \u26A0\uFE0F Component ${componentId} not available`));
9653
8450
  }
9654
8451
  }
9655
8452
  console.log("");
@@ -9660,11 +8457,11 @@ async function chatCommand(message, options) {
9660
8457
  const toInstallNpm = [...neededPkgs].filter((p) => !installedPkgs.has(p));
9661
8458
  if (toInstallNpm.length > 0) {
9662
8459
  spinner.stop();
9663
- console.log(chalk14.cyan(`
8460
+ console.log(chalk13.cyan(`
9664
8461
  \u{1F4E6} Auto-installing missing dependencies: ${toInstallNpm.join(", ")}
9665
8462
  `));
9666
8463
  const ok = await installPackages(projectRoot, toInstallNpm);
9667
- if (!ok) console.log(chalk14.yellow(` Run manually: npm install ${toInstallNpm.join(" ")}
8464
+ if (!ok) console.log(chalk13.yellow(` Run manually: npm install ${toInstallNpm.join(" ")}
9668
8465
  `));
9669
8466
  spinner.start("Applying modifications...");
9670
8467
  }
@@ -9675,7 +8472,7 @@ async function chatCommand(message, options) {
9675
8472
  if (componentId && preflightComponentIds.has(componentId)) {
9676
8473
  if (DEBUG4) {
9677
8474
  console.log(
9678
- chalk14.gray(`[DEBUG] Filtered duplicate add-component: ${componentId} (already installed in pre-flight)`)
8475
+ chalk13.gray(`[DEBUG] Filtered duplicate add-component: ${componentId} (already installed in pre-flight)`)
9679
8476
  );
9680
8477
  }
9681
8478
  return false;
@@ -9684,11 +8481,11 @@ async function chatCommand(message, options) {
9684
8481
  return true;
9685
8482
  });
9686
8483
  if (DEBUG4 && preflightComponentIds.size > 0) {
9687
- console.log(chalk14.gray(`[DEBUG] Remaining requests after filtering: ${normalizedRequests.length}`));
8484
+ console.log(chalk13.gray(`[DEBUG] Remaining requests after filtering: ${normalizedRequests.length}`));
9688
8485
  }
9689
8486
  try {
9690
8487
  createBackup(projectRoot);
9691
- if (DEBUG4) console.log(chalk14.dim("[backup] Created snapshot"));
8488
+ if (DEBUG4) console.log(chalk13.dim("[backup] Created snapshot"));
9692
8489
  } catch {
9693
8490
  }
9694
8491
  const navBefore = takeNavSnapshot(
@@ -9711,6 +8508,26 @@ async function chatCommand(message, options) {
9711
8508
  break;
9712
8509
  }
9713
8510
  }
8511
+ try {
8512
+ const { validateReuse } = await import("./reuse-validator-HC4LZEKF.js");
8513
+ const { inferPageTypeFromRoute: inferPageTypeFromRoute2 } = await import("./design-constraints-EIP2XM7T.js");
8514
+ const manifest2 = await loadManifest8(projectRoot);
8515
+ if (manifest2.shared.length > 0) {
8516
+ for (const request of normalizedRequests) {
8517
+ if (request.type !== "add-page") continue;
8518
+ const changes = request.changes;
8519
+ const pageCode = changes?.pageCode;
8520
+ if (!pageCode) continue;
8521
+ const route = changes.route || "";
8522
+ const pageType = inferPageTypeFromRoute2(route);
8523
+ const warnings = validateReuse(manifest2, pageCode, pageType);
8524
+ for (const w of warnings) {
8525
+ console.log(chalk13.yellow(` \u26A0 ${w.message}`));
8526
+ }
8527
+ }
8528
+ }
8529
+ } catch {
8530
+ }
9714
8531
  const currentConfig = dsm.getConfig();
9715
8532
  const autoScaffoldEnabled = currentConfig.settings.autoScaffold === true;
9716
8533
  const scaffoldedPages = [];
@@ -9751,10 +8568,10 @@ async function chatCommand(message, options) {
9751
8568
  const SCAFFOLD_AI_LIMIT = 10;
9752
8569
  if (missingRoutes.length > 0 && missingRoutes.length <= SCAFFOLD_AI_LIMIT) {
9753
8570
  spinner.stop();
9754
- console.log(chalk14.cyan(`
8571
+ console.log(chalk13.cyan(`
9755
8572
  \u{1F517} Auto-scaffolding ${missingRoutes.length} linked page(s)...`));
9756
8573
  console.log(
9757
- chalk14.dim(
8574
+ chalk13.dim(
9758
8575
  ` (${missingRoutes.length} additional AI call(s) \u2014 disable with settings.autoScaffold: false in config)
9759
8576
  `
9760
8577
  )
@@ -9791,7 +8608,7 @@ async function chatCommand(message, options) {
9791
8608
  }
9792
8609
  } catch (err) {
9793
8610
  scaffoldSpinner.warn(` Could not scaffold "${pageName}" (${linkedRoute}) \u2014 skipped`);
9794
- if (DEBUG4) console.log(chalk14.dim(` ${err instanceof Error ? err.message : "unknown error"}`));
8611
+ if (DEBUG4) console.log(chalk13.dim(` ${err instanceof Error ? err.message : "unknown error"}`));
9795
8612
  }
9796
8613
  }
9797
8614
  console.log("");
@@ -9799,7 +8616,7 @@ async function chatCommand(message, options) {
9799
8616
  } else if (missingRoutes.length > SCAFFOLD_AI_LIMIT) {
9800
8617
  spinner.stop();
9801
8618
  console.log(
9802
- chalk14.yellow(
8619
+ chalk13.yellow(
9803
8620
  `
9804
8621
  \u26A0 Found ${missingRoutes.length} linked pages \u2014 creating placeholder pages (too many for AI generation).`
9805
8622
  )
@@ -9828,7 +8645,7 @@ async function chatCommand(message, options) {
9828
8645
  scaffoldedPages.push({ route: linkedRoute, name: `${pageName} (placeholder)` });
9829
8646
  }
9830
8647
  console.log(
9831
- chalk14.cyan(` Created ${missingRoutes.length} placeholder pages. Use \`coherent chat\` to fill them.
8648
+ chalk13.cyan(` Created ${missingRoutes.length} placeholder pages. Use \`coherent chat\` to fill them.
9832
8649
  `)
9833
8650
  );
9834
8651
  spinner.start("Finalizing...");
@@ -9856,9 +8673,9 @@ async function chatCommand(message, options) {
9856
8673
  }
9857
8674
  }
9858
8675
  if (linkIssues.length > 0) {
9859
- console.log(chalk14.yellow("\n\u{1F517} Broken internal links:"));
8676
+ console.log(chalk13.yellow("\n\u{1F517} Broken internal links:"));
9860
8677
  for (const { page, message: message2 } of linkIssues) {
9861
- console.log(chalk14.dim(` ${page}: ${message2}`));
8678
+ console.log(chalk13.dim(` ${page}: ${message2}`));
9862
8679
  }
9863
8680
  }
9864
8681
  }
@@ -9871,7 +8688,7 @@ async function chatCommand(message, options) {
9871
8688
  if (latestConfig.theme.defaultMode !== targetMode) {
9872
8689
  latestConfig.theme.defaultMode = targetMode;
9873
8690
  dsm.updateConfig(latestConfig);
9874
- if (DEBUG4) console.log(chalk14.dim(` [theme] Set defaultMode to "${targetMode}"`));
8691
+ if (DEBUG4) console.log(chalk13.dim(` [theme] Set defaultMode to "${targetMode}"`));
9875
8692
  }
9876
8693
  const layoutPath = resolve10(projectRoot, "app", "layout.tsx");
9877
8694
  try {
@@ -9879,11 +8696,11 @@ async function chatCommand(message, options) {
9879
8696
  if (targetMode === "dark" && !layoutCode.includes('className="dark"')) {
9880
8697
  layoutCode = layoutCode.replace(/<html\s+lang="en"/, '<html lang="en" className="dark"');
9881
8698
  await writeFile(layoutPath, layoutCode);
9882
- console.log(chalk14.dim(` \u{1F319} Applied dark theme to layout`));
8699
+ console.log(chalk13.dim(` \u{1F319} Applied dark theme to layout`));
9883
8700
  } else if (targetMode === "light" && layoutCode.includes('className="dark"')) {
9884
8701
  layoutCode = layoutCode.replace(' className="dark"', "");
9885
8702
  await writeFile(layoutPath, layoutCode);
9886
- console.log(chalk14.dim(` \u2600\uFE0F Applied light theme to layout`));
8703
+ console.log(chalk13.dim(` \u2600\uFE0F Applied light theme to layout`));
9887
8704
  }
9888
8705
  } catch {
9889
8706
  }
@@ -9915,7 +8732,7 @@ async function chatCommand(message, options) {
9915
8732
  }
9916
8733
  const finalDeps = await scanAndInstallSharedDeps(projectRoot);
9917
8734
  if (finalDeps.length > 0) {
9918
- console.log(chalk14.dim(` Auto-installed shared deps: ${finalDeps.join(", ")}`));
8735
+ console.log(chalk13.dim(` Auto-installed shared deps: ${finalDeps.join(", ")}`));
9919
8736
  }
9920
8737
  try {
9921
8738
  fixGlobalsCss(projectRoot, updatedConfig);
@@ -9939,7 +8756,53 @@ async function chatCommand(message, options) {
9939
8756
  }
9940
8757
  await saveHashes(projectRoot, updatedHashes);
9941
8758
  } catch {
9942
- if (DEBUG4) console.log(chalk14.dim("[hashes] Could not save file hashes"));
8759
+ if (DEBUG4) console.log(chalk13.dim("[hashes] Could not save file hashes"));
8760
+ }
8761
+ try {
8762
+ const { extractPropsInterface, extractDependencies, extractUsageExample } = await import("./component-extractor-VYJLT5NR.js");
8763
+ let currentManifest = await loadManifest8(projectRoot);
8764
+ let manifestChanged = false;
8765
+ for (const entry of currentManifest.shared) {
8766
+ const fullPath = resolve10(projectRoot, entry.file);
8767
+ if (!existsSync17(fullPath)) continue;
8768
+ const code = readFileSync12(fullPath, "utf-8");
8769
+ const props = extractPropsInterface(code);
8770
+ const deps = extractDependencies(code);
8771
+ if (props && props !== entry.propsInterface || deps.length !== (entry.dependencies?.length ?? 0)) {
8772
+ currentManifest = updateEntry(currentManifest, entry.id, {
8773
+ propsInterface: props ?? entry.propsInterface,
8774
+ dependencies: deps
8775
+ });
8776
+ manifestChanged = true;
8777
+ }
8778
+ }
8779
+ const pageFiles = Array.from(allModified).filter((f) => f.startsWith("app/") && f.endsWith("page.tsx"));
8780
+ for (const pageFile of pageFiles) {
8781
+ const fullPath = resolve10(projectRoot, pageFile);
8782
+ if (!existsSync17(fullPath)) continue;
8783
+ const pageCode = readFileSync12(fullPath, "utf-8");
8784
+ for (const entry of currentManifest.shared) {
8785
+ const isUsed = pageCode.includes(`from '@/components/shared/`) && (pageCode.includes(`{ ${entry.name} }`) || pageCode.includes(`{ ${entry.name},`));
8786
+ if (isUsed && !entry.usedIn.includes(pageFile)) {
8787
+ currentManifest = updateEntry(currentManifest, entry.id, {
8788
+ usedIn: [...entry.usedIn, pageFile]
8789
+ });
8790
+ manifestChanged = true;
8791
+ if (!entry.usageExample) {
8792
+ const usage = extractUsageExample(pageCode, entry.name);
8793
+ if (usage) {
8794
+ currentManifest = updateEntry(currentManifest, entry.id, { usageExample: usage });
8795
+ }
8796
+ }
8797
+ }
8798
+ }
8799
+ }
8800
+ if (manifestChanged) {
8801
+ await saveManifest2(projectRoot, currentManifest);
8802
+ if (DEBUG4) console.log(chalk13.dim("[auto-sync] Manifest updated"));
8803
+ }
8804
+ } catch {
8805
+ if (DEBUG4) console.log(chalk13.dim("[auto-sync] Skipped"));
9943
8806
  }
9944
8807
  const successfulPairs = normalizedRequests.map((request, index) => ({ request, result: results[index] })).filter(({ result }) => result.success);
9945
8808
  if (successfulPairs.length > 0) {
@@ -9957,9 +8820,9 @@ async function chatCommand(message, options) {
9957
8820
  showPreview(normalizedRequests, results, updatedConfig, preflightNames);
9958
8821
  if (scaffoldedPages.length > 0) {
9959
8822
  const uniqueScaffolded = [...new Map(scaffoldedPages.map((s) => [s.route, s])).values()];
9960
- console.log(chalk14.cyan("\u{1F517} Auto-scaffolded linked pages:"));
8823
+ console.log(chalk13.cyan("\u{1F517} Auto-scaffolded linked pages:"));
9961
8824
  uniqueScaffolded.forEach(({ route, name }) => {
9962
- console.log(chalk14.white(` \u2728 ${name} \u2192 ${route}`));
8825
+ console.log(chalk13.white(` \u2728 ${name} \u2192 ${route}`));
9963
8826
  });
9964
8827
  console.log("");
9965
8828
  }
@@ -9981,58 +8844,58 @@ ${uxRecommendations}
9981
8844
  );
9982
8845
  }
9983
8846
  await appendFile(recPath, section);
9984
- console.log(chalk14.cyan("\n\u{1F4CB} UX Recommendations:"));
8847
+ console.log(chalk13.cyan("\n\u{1F4CB} UX Recommendations:"));
9985
8848
  for (const line of uxRecommendations.split("\n").filter(Boolean)) {
9986
- console.log(chalk14.dim(` ${line}`));
8849
+ console.log(chalk13.dim(` ${line}`));
9987
8850
  }
9988
- console.log(chalk14.dim(" \u2192 Saved to /design-system/docs/recommendations"));
8851
+ console.log(chalk13.dim(" \u2192 Saved to /design-system/docs/recommendations"));
9989
8852
  } catch (e) {
9990
8853
  console.log(
9991
- chalk14.yellow("\n\u26A0\uFE0F Could not write recommendations.md: " + (e instanceof Error ? e.message : String(e)))
8854
+ chalk13.yellow("\n\u26A0\uFE0F Could not write recommendations.md: " + (e instanceof Error ? e.message : String(e)))
9992
8855
  );
9993
- console.log(chalk14.dim("Recommendations:\n") + uxRecommendations);
8856
+ console.log(chalk13.dim("Recommendations:\n") + uxRecommendations);
9994
8857
  }
9995
8858
  }
9996
8859
  } catch (error) {
9997
8860
  spinner.fail("Chat command failed");
9998
- console.error(chalk14.red("\n\u2716 Chat command failed"));
8861
+ console.error(chalk13.red("\n\u2716 Chat command failed"));
9999
8862
  const zodError = error;
10000
8863
  const issues = zodError.issues || error.errors;
10001
8864
  if (issues && Array.isArray(issues)) {
10002
- console.log(chalk14.yellow("\n\u26A0\uFE0F AI generated incomplete data. Missing or invalid fields:"));
8865
+ console.log(chalk13.yellow("\n\u26A0\uFE0F AI generated incomplete data. Missing or invalid fields:"));
10003
8866
  issues.forEach((err) => {
10004
- console.log(chalk14.gray(` \u2022 ${err.path.join(".")}: ${err.message}`));
8867
+ console.log(chalk13.gray(` \u2022 ${err.path.join(".")}: ${err.message}`));
10005
8868
  });
10006
- console.log(chalk14.cyan("\n\u{1F4A1} Try being more specific, e.g.:"));
10007
- console.log(chalk14.white(' coherent chat "add a dashboard page with hero section using Button component"'));
10008
- console.log(chalk14.white(' coherent chat "add pricing page"'));
8869
+ console.log(chalk13.cyan("\n\u{1F4A1} Try being more specific, e.g.:"));
8870
+ console.log(chalk13.white(' coherent chat "add a dashboard page with hero section using Button component"'));
8871
+ console.log(chalk13.white(' coherent chat "add pricing page"'));
10009
8872
  } else if (error instanceof Error) {
10010
- console.error(chalk14.red(error.message));
8873
+ console.error(chalk13.red(error.message));
10011
8874
  if (error.message.includes("Unterminated string") || error.message.includes("Unexpected end of JSON") || error.message.includes("Failed to parse modification") && error.message.includes("JSON")) {
10012
8875
  console.log(
10013
- chalk14.yellow("\n\u{1F4A1} The AI response was too large or contained invalid JSON. Try splitting your request:")
8876
+ chalk13.yellow("\n\u{1F4A1} The AI response was too large or contained invalid JSON. Try splitting your request:")
10014
8877
  );
10015
- console.log(chalk14.white(' coherent chat "add dashboard page with stats and recent activity"'));
10016
- console.log(chalk14.white(' coherent chat "add account page"'));
10017
- console.log(chalk14.white(' coherent chat "add settings page"'));
8878
+ console.log(chalk13.white(' coherent chat "add dashboard page with stats and recent activity"'));
8879
+ console.log(chalk13.white(' coherent chat "add account page"'));
8880
+ console.log(chalk13.white(' coherent chat "add settings page"'));
10018
8881
  } else if (error.message.includes("API key not found") || error.message.includes("ANTHROPIC_API_KEY") || error.message.includes("OPENAI_API_KEY")) {
10019
8882
  const isOpenAI = error.message.includes("OpenAI") || typeof provider !== "undefined" && provider === "openai";
10020
8883
  const providerName = isOpenAI ? "OpenAI" : "Anthropic Claude";
10021
8884
  const envVar = isOpenAI ? "OPENAI_API_KEY" : "ANTHROPIC_API_KEY";
10022
8885
  const url = isOpenAI ? "https://platform.openai.com" : "https://console.anthropic.com";
10023
- console.log(chalk14.yellow("\n\u{1F4A1} Setup Instructions:"));
10024
- console.log(chalk14.dim(` 1. Get your ${providerName} API key from: ${url}`));
10025
- console.log(chalk14.dim(" 2. Create a .env file in the current directory:"));
10026
- console.log(chalk14.cyan(` echo "${envVar}=your_key_here" > .env`));
10027
- console.log(chalk14.dim(" 3. Or export it in your shell:"));
10028
- console.log(chalk14.cyan(` export ${envVar}=your_key_here`));
8886
+ console.log(chalk13.yellow("\n\u{1F4A1} Setup Instructions:"));
8887
+ console.log(chalk13.dim(` 1. Get your ${providerName} API key from: ${url}`));
8888
+ console.log(chalk13.dim(" 2. Create a .env file in the current directory:"));
8889
+ console.log(chalk13.cyan(` echo "${envVar}=your_key_here" > .env`));
8890
+ console.log(chalk13.dim(" 3. Or export it in your shell:"));
8891
+ console.log(chalk13.cyan(` export ${envVar}=your_key_here`));
10029
8892
  if (isOpenAI) {
10030
- console.log(chalk14.dim('\n Also ensure "openai" package is installed:'));
10031
- console.log(chalk14.cyan(" npm install openai"));
8893
+ console.log(chalk13.dim('\n Also ensure "openai" package is installed:'));
8894
+ console.log(chalk13.cyan(" npm install openai"));
10032
8895
  }
10033
8896
  }
10034
8897
  } else {
10035
- console.error(chalk14.red("Unknown error occurred"));
8898
+ console.error(chalk13.red("Unknown error occurred"));
10036
8899
  }
10037
8900
  console.log("");
10038
8901
  if (options._throwOnError) {
@@ -10045,7 +8908,7 @@ ${uxRecommendations}
10045
8908
  }
10046
8909
 
10047
8910
  // src/commands/preview.ts
10048
- import chalk15 from "chalk";
8911
+ import chalk14 from "chalk";
10049
8912
  import ora3 from "ora";
10050
8913
  import { spawn } from "child_process";
10051
8914
  import { existsSync as existsSync20, rmSync as rmSync3, readFileSync as readFileSync15, writeFileSync as writeFileSync10, readdirSync as readdirSync6 } from "fs";
@@ -10122,9 +8985,15 @@ function extractExportedComponentNames(code) {
10122
8985
  }
10123
8986
  function inferComponentType(name, code) {
10124
8987
  const lower = name.toLowerCase();
10125
- if (/header|footer|sidebar|nav|layout|appbar|topbar/.test(lower)) return "layout";
8988
+ if (/header|footer|layout|appbar|topbar/.test(lower)) return "layout";
8989
+ if (/sidebar|nav|menu|breadcrumb|tabs/.test(lower)) return "navigation";
8990
+ if (/table|chart|stats|metric|list|grid|card/.test(lower)) return "data-display";
8991
+ if (/form|input|filter|search|select|picker/.test(lower)) return "form";
8992
+ if (/alert|toast|modal|dialog|notification|error|snackbar/.test(lower)) return "feedback";
10126
8993
  if (/section|hero|pricing|testimonial|features|cta|banner/.test(lower)) return "section";
10127
8994
  if (/<header[\s>]/.test(code) || /<footer[\s>]/.test(code)) return "layout";
8995
+ if (/<nav[\s>]/.test(code)) return "navigation";
8996
+ if (/<form[\s>]/.test(code)) return "form";
10128
8997
  if (/<section[\s>]/.test(code)) return "section";
10129
8998
  return "widget";
10130
8999
  }
@@ -10334,7 +9203,9 @@ function reconcileComponents(projectRoot, manifest) {
10334
9203
  file: comp.file,
10335
9204
  usedIn: comp.usedIn,
10336
9205
  description: `Auto-registered by sync from ${comp.file}`,
10337
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
9206
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
9207
+ dependencies: [],
9208
+ source: "extracted"
10338
9209
  });
10339
9210
  m.nextId++;
10340
9211
  result.added.push({ id, name: comp.name, file: comp.file, type: comp.type });
@@ -10435,13 +9306,13 @@ async function handleFileChange(projectRoot, filePath) {
10435
9306
  return;
10436
9307
  }
10437
9308
  const config2 = getWatcherConfig(projectRoot);
10438
- const chalk34 = (await import("chalk")).default;
9309
+ const chalk33 = (await import("chalk")).default;
10439
9310
  if (config2.autoInstall) {
10440
9311
  const missing = findMissingPackagesInCode(content, projectRoot);
10441
9312
  if (missing.length > 0) {
10442
9313
  const ok = await installPackages(projectRoot, missing);
10443
9314
  if (ok) {
10444
- console.log(chalk34.cyan(`
9315
+ console.log(chalk33.cyan(`
10445
9316
  \u{1F527} Auto-installed: ${missing.join(", ")} (needed by ${relativePath})`));
10446
9317
  }
10447
9318
  }
@@ -10450,12 +9321,12 @@ async function handleFileChange(projectRoot, filePath) {
10450
9321
  const fixed = sanitizeMetadataStrings(ensureUseClientIfNeeded(content));
10451
9322
  if (fixed !== content) {
10452
9323
  writeFileSync9(filePath, fixed, "utf-8");
10453
- console.log(chalk34.cyan(` \u{1F527} Auto-fixed syntax in ${relativePath}`));
9324
+ console.log(chalk33.cyan(` \u{1F527} Auto-fixed syntax in ${relativePath}`));
10454
9325
  }
10455
9326
  }
10456
9327
  if (config2.warnNativeElements && hasNativeElements(content)) {
10457
- console.log(chalk34.yellow(` \u26A0 ${relativePath}: uses native HTML elements (<button>, <select>, etc.)`));
10458
- console.log(chalk34.dim(" Use components from @/components/ui/ instead"));
9328
+ console.log(chalk33.yellow(` \u26A0 ${relativePath}: uses native HTML elements (<button>, <select>, etc.)`));
9329
+ console.log(chalk33.dim(" Use components from @/components/ui/ instead"));
10459
9330
  }
10460
9331
  if (config2.warnSharedReuse) {
10461
9332
  let manifest;
@@ -10468,8 +9339,8 @@ async function handleFileChange(projectRoot, filePath) {
10468
9339
  const dupes = findInlineDuplicatesOfShared(content, manifest);
10469
9340
  for (const d of dupes) {
10470
9341
  const importPath = d.file.replace(/\.tsx$/, "").replace(/^components\/shared\//, "");
10471
- console.log(chalk34.yellow(` \u26A0 ${relativePath}: has inline code similar to ${d.cid} (${d.name})`));
10472
- console.log(chalk34.dim(` Consider: import { ${d.name} } from "@/components/shared/${importPath}"`));
9342
+ console.log(chalk33.yellow(` \u26A0 ${relativePath}: has inline code similar to ${d.cid} (${d.name})`));
9343
+ console.log(chalk33.dim(` Consider: import { ${d.name} } from "@/components/shared/${importPath}"`));
10473
9344
  }
10474
9345
  }
10475
9346
  }
@@ -10478,7 +9349,7 @@ async function handleFileDelete(projectRoot, filePath) {
10478
9349
  const relativePath = relative4(projectRoot, filePath).replace(/\\/g, "/");
10479
9350
  if (!relativePath.startsWith("components/") || relativePath.startsWith("components/ui/")) return;
10480
9351
  try {
10481
- const chalk34 = (await import("chalk")).default;
9352
+ const chalk33 = (await import("chalk")).default;
10482
9353
  const manifest = await loadManifest9(projectRoot);
10483
9354
  const orphaned = manifest.shared.find((s) => s.file === relativePath);
10484
9355
  if (orphaned) {
@@ -10487,7 +9358,7 @@ async function handleFileDelete(projectRoot, filePath) {
10487
9358
  shared: manifest.shared.filter((s) => s.id !== orphaned.id)
10488
9359
  };
10489
9360
  await saveManifest3(projectRoot, cleaned);
10490
- console.log(chalk34.cyan(`
9361
+ console.log(chalk33.cyan(`
10491
9362
  \u{1F5D1} Auto-removed ${orphaned.id} (${orphaned.name}) \u2014 file deleted`));
10492
9363
  await writeCursorRules(projectRoot);
10493
9364
  }
@@ -10499,7 +9370,7 @@ async function detectNewComponent(projectRoot, filePath) {
10499
9370
  if (!relativePath.startsWith("components/") || relativePath.startsWith("components/ui/")) return;
10500
9371
  if (!relativePath.endsWith(".tsx") && !relativePath.endsWith(".jsx")) return;
10501
9372
  try {
10502
- const chalk34 = (await import("chalk")).default;
9373
+ const chalk33 = (await import("chalk")).default;
10503
9374
  const manifest = await loadManifest9(projectRoot);
10504
9375
  const alreadyRegistered = manifest.shared.some((s) => s.file === relativePath);
10505
9376
  if (alreadyRegistered) return;
@@ -10508,9 +9379,9 @@ async function detectNewComponent(projectRoot, filePath) {
10508
9379
  if (exports.length > 0) {
10509
9380
  const alreadyByName = exports.every((n) => manifest.shared.some((s) => s.name === n));
10510
9381
  if (!alreadyByName) {
10511
- console.log(chalk34.cyan(`
9382
+ console.log(chalk33.cyan(`
10512
9383
  \u2139 New component detected: ${exports[0]} in ${relativePath}`));
10513
- console.log(chalk34.dim(" Register with: coherent sync"));
9384
+ console.log(chalk33.dim(" Register with: coherent sync"));
10514
9385
  }
10515
9386
  }
10516
9387
  } catch {
@@ -10597,17 +9468,17 @@ function clearStaleCache(projectRoot) {
10597
9468
  const nextDir = join14(projectRoot, ".next");
10598
9469
  if (existsSync20(nextDir)) {
10599
9470
  rmSync3(nextDir, { recursive: true, force: true });
10600
- console.log(chalk15.dim(" \u2714 Cleared stale build cache"));
9471
+ console.log(chalk14.dim(" \u2714 Cleared stale build cache"));
10601
9472
  }
10602
9473
  }
10603
9474
  async function preflightDependencyCheck(projectRoot) {
10604
9475
  const missing = await findMissingPackages(projectRoot);
10605
9476
  if (missing.length === 0) return;
10606
- console.log(chalk15.cyan(`
9477
+ console.log(chalk14.cyan(`
10607
9478
  Auto-installing missing dependencies: ${missing.join(", ")}`));
10608
9479
  const ok = await installPackages(projectRoot, missing);
10609
- if (ok) console.log(chalk15.dim(" \u2714 Installed"));
10610
- else console.log(chalk15.yellow(` Run manually: npm install ${missing.join(" ")}`));
9480
+ if (ok) console.log(chalk14.dim(" \u2714 Installed"));
9481
+ else console.log(chalk14.yellow(` Run manually: npm install ${missing.join(" ")}`));
10611
9482
  }
10612
9483
  async function listPageFiles(appDir) {
10613
9484
  const out = [];
@@ -10631,7 +9502,7 @@ async function validateSyntax(projectRoot) {
10631
9502
  const fixed = fixUnescapedLtInJsx(sanitizeMetadataStrings(ensureUseClientIfNeeded(content)));
10632
9503
  if (fixed !== content) {
10633
9504
  writeFileSync10(file, fixed, "utf-8");
10634
- console.log(chalk15.dim(` \u2714 Auto-fixed syntax: ${file.replace(projectRoot, ".").replace(/^\.[/\\]/, "")}`));
9505
+ console.log(chalk14.dim(` \u2714 Auto-fixed syntax: ${file.replace(projectRoot, ".").replace(/^\.[/\\]/, "")}`));
10635
9506
  }
10636
9507
  }
10637
9508
  }
@@ -10673,7 +9544,7 @@ async function fixMissingComponentExports(projectRoot) {
10673
9544
  try {
10674
9545
  const result = await provider.installComponent(componentId, projectRoot);
10675
9546
  if (result.success) {
10676
- console.log(chalk15.dim(` \u2714 Installed missing ${componentId}.tsx`));
9547
+ console.log(chalk14.dim(` \u2714 Installed missing ${componentId}.tsx`));
10677
9548
  }
10678
9549
  } catch {
10679
9550
  }
@@ -10685,7 +9556,7 @@ async function fixMissingComponentExports(projectRoot) {
10685
9556
  mkdirSync10(uiDir, { recursive: true });
10686
9557
  const newContent = await generator.generate(def);
10687
9558
  writeFileSync10(componentFile, newContent, "utf-8");
10688
- console.log(chalk15.dim(` \u2714 Created missing ${componentId}.tsx`));
9559
+ console.log(chalk14.dim(` \u2714 Created missing ${componentId}.tsx`));
10689
9560
  } catch {
10690
9561
  }
10691
9562
  }
@@ -10708,7 +9579,7 @@ async function fixMissingComponentExports(projectRoot) {
10708
9579
  try {
10709
9580
  const result = await provider.installComponent(componentId, projectRoot, { force: true });
10710
9581
  if (result.success) {
10711
- console.log(chalk15.dim(` \u2714 Reinstalled ${componentId}.tsx (added missing exports: ${missing.join(", ")})`));
9582
+ console.log(chalk14.dim(` \u2714 Reinstalled ${componentId}.tsx (added missing exports: ${missing.join(", ")})`));
10712
9583
  }
10713
9584
  } catch {
10714
9585
  }
@@ -10718,7 +9589,7 @@ async function fixMissingComponentExports(projectRoot) {
10718
9589
  try {
10719
9590
  const newContent = await generator.generate(def);
10720
9591
  writeFileSync10(componentFile, newContent, "utf-8");
10721
- console.log(chalk15.dim(` \u2714 Regenerated ${componentId}.tsx (added missing exports: ${missing.join(", ")})`));
9592
+ console.log(chalk14.dim(` \u2714 Regenerated ${componentId}.tsx (added missing exports: ${missing.join(", ")})`));
10722
9593
  } catch {
10723
9594
  }
10724
9595
  }
@@ -10777,14 +9648,14 @@ async function healthCheck(port) {
10777
9648
  try {
10778
9649
  const res = await fetch(`http://localhost:${port}`);
10779
9650
  if (res.status === 200) {
10780
- console.log(chalk15.green(`
9651
+ console.log(chalk14.green(`
10781
9652
  \u2705 Preview healthy at http://localhost:${port}`));
10782
9653
  } else {
10783
- console.log(chalk15.yellow(`
9654
+ console.log(chalk14.yellow(`
10784
9655
  \u26A0 Preview returned ${res.status}. Run: coherent fix`));
10785
9656
  }
10786
9657
  } catch {
10787
- console.log(chalk15.yellow(`
9658
+ console.log(chalk14.yellow(`
10788
9659
  \u26A0 Preview not responding. Run: coherent fix`));
10789
9660
  }
10790
9661
  }
@@ -10823,29 +9694,29 @@ function launchWithMonitoring(projectRoot, restarts) {
10823
9694
  const shadcnId = extractShadcnComponentFromModuleNotFound(msg);
10824
9695
  if (shadcnId && !installingSet.has(shadcnId)) {
10825
9696
  installingSet.add(shadcnId);
10826
- console.log(chalk15.yellow(`
9697
+ console.log(chalk14.yellow(`
10827
9698
  \u26A0 Missing component detected: ${shadcnId}`));
10828
- console.log(chalk15.cyan(" Auto-installing..."));
9699
+ console.log(chalk14.cyan(" Auto-installing..."));
10829
9700
  autoInstallShadcnComponent(shadcnId, projectRoot).then((ok) => {
10830
9701
  if (ok) {
10831
- console.log(chalk15.green(` \u2714 Installed ${shadcnId}.tsx. Restarting...`));
9702
+ console.log(chalk14.green(` \u2714 Installed ${shadcnId}.tsx. Restarting...`));
10832
9703
  intentionalRestart = true;
10833
9704
  server.kill("SIGTERM");
10834
9705
  launchWithMonitoring(projectRoot, restarts + 1).then(resolvePromise).catch(rejectPromise);
10835
9706
  } else {
10836
- console.log(chalk15.red(` \u2716 Could not install ${shadcnId}. Run: npx shadcn@latest add ${shadcnId}`));
9707
+ console.log(chalk14.red(` \u2716 Could not install ${shadcnId}. Run: npx shadcn@latest add ${shadcnId}`));
10837
9708
  }
10838
9709
  });
10839
9710
  } else if (!shadcnId) {
10840
9711
  const pkg = extractPackageFromModuleNotFound(msg);
10841
9712
  if (pkg && !installingSet.has(pkg)) {
10842
9713
  installingSet.add(pkg);
10843
- console.log(chalk15.yellow(`
9714
+ console.log(chalk14.yellow(`
10844
9715
  \u26A0 Missing package detected: ${pkg}`));
10845
- console.log(chalk15.cyan(" Auto-installing..."));
9716
+ console.log(chalk14.cyan(" Auto-installing..."));
10846
9717
  installPackages(projectRoot, [pkg]).then((ok) => {
10847
9718
  if (ok) {
10848
- console.log(chalk15.green(` \u2714 Installed ${pkg}. Restarting...`));
9719
+ console.log(chalk14.green(` \u2714 Installed ${pkg}. Restarting...`));
10849
9720
  intentionalRestart = true;
10850
9721
  server.kill("SIGTERM");
10851
9722
  launchWithMonitoring(projectRoot, restarts + 1).then(resolvePromise).catch(rejectPromise);
@@ -10855,19 +9726,19 @@ function launchWithMonitoring(projectRoot, restarts) {
10855
9726
  }
10856
9727
  }
10857
9728
  if (msg.includes("Failed to compile")) {
10858
- console.log(chalk15.yellow("\n\u26A0 Compilation error detected."));
10859
- console.log(chalk15.dim(' Hint: run "coherent fix" in another terminal to auto-fix'));
10860
- console.log(chalk15.dim(' Or: coherent chat "fix the broken page"'));
9729
+ console.log(chalk14.yellow("\n\u26A0 Compilation error detected."));
9730
+ console.log(chalk14.dim(' Hint: run "coherent fix" in another terminal to auto-fix'));
9731
+ console.log(chalk14.dim(' Or: coherent chat "fix the broken page"'));
10861
9732
  }
10862
9733
  });
10863
9734
  server.on("exit", (code) => {
10864
9735
  if (intentionalRestart) return;
10865
9736
  if (code !== 0 && code !== null) {
10866
- console.log(chalk15.red(`
9737
+ console.log(chalk14.red(`
10867
9738
  \u274C Dev server exited with code ${code}`));
10868
- console.log(chalk15.dim(' Check the output above. Fix and run "coherent preview" again.\n'));
9739
+ console.log(chalk14.dim(' Check the output above. Fix and run "coherent preview" again.\n'));
10869
9740
  } else {
10870
- console.log(chalk15.dim("\n\u{1F44B} Dev server stopped"));
9741
+ console.log(chalk14.dim("\n\u{1F44B} Dev server stopped"));
10871
9742
  }
10872
9743
  process.exit(code ?? 0);
10873
9744
  });
@@ -10876,7 +9747,7 @@ function launchWithMonitoring(projectRoot, restarts) {
10876
9747
  });
10877
9748
  const shutdown = () => {
10878
9749
  closeWatcher();
10879
- console.log(chalk15.dim("\n\n\u{1F6D1} Stopping dev server..."));
9750
+ console.log(chalk14.dim("\n\n\u{1F6D1} Stopping dev server..."));
10880
9751
  server.kill("SIGTERM");
10881
9752
  };
10882
9753
  process.on("SIGINT", shutdown);
@@ -10888,9 +9759,9 @@ async function openBrowser(url) {
10888
9759
  const open = await import("open");
10889
9760
  await open.default(url);
10890
9761
  } catch (error) {
10891
- console.log(chalk15.yellow(`
9762
+ console.log(chalk14.yellow(`
10892
9763
  \u26A0\uFE0F Could not open browser automatically`));
10893
- console.log(chalk15.dim(` Please open ${url} manually`));
9764
+ console.log(chalk14.dim(` Please open ${url} manually`));
10894
9765
  }
10895
9766
  }
10896
9767
  function startDevServer(projectRoot) {
@@ -10921,8 +9792,8 @@ async function previewCommand() {
10921
9792
  try {
10922
9793
  if (!checkProjectInitialized(projectRoot)) {
10923
9794
  spinner.fail("Project not initialized");
10924
- console.log(chalk15.red("\n\u274C Project not found"));
10925
- console.log(chalk15.dim('Run "coherent init" first to create a project.'));
9795
+ console.log(chalk14.red("\n\u274C Project not found"));
9796
+ console.log(chalk14.dim('Run "coherent init" first to create a project.'));
10926
9797
  process.exit(1);
10927
9798
  }
10928
9799
  spinner.text = "Checking dependencies...";
@@ -10930,16 +9801,16 @@ async function previewCommand() {
10930
9801
  spinner.warn("Dependencies not installed");
10931
9802
  const pm = getPackageManager(projectRoot);
10932
9803
  const installCommand = pm === "pnpm" ? "pnpm install" : "npm install";
10933
- console.log(chalk15.yellow("\n\u26A0\uFE0F Dependencies not installed"));
10934
- console.log(chalk15.cyan(`
9804
+ console.log(chalk14.yellow("\n\u26A0\uFE0F Dependencies not installed"));
9805
+ console.log(chalk14.cyan(`
10935
9806
  Running ${installCommand}...
10936
9807
  `));
10937
9808
  const ok = await runInstall(projectRoot);
10938
9809
  if (!ok) {
10939
- console.error(chalk15.red('\n\u274C Install failed. Fix errors above and run "coherent preview" again.\n'));
9810
+ console.error(chalk14.red('\n\u274C Install failed. Fix errors above and run "coherent preview" again.\n'));
10940
9811
  process.exit(1);
10941
9812
  }
10942
- console.log(chalk15.green("\n\u2705 Dependencies installed\n"));
9813
+ console.log(chalk14.green("\n\u2705 Dependencies installed\n"));
10943
9814
  } else {
10944
9815
  spinner.succeed("Dependencies installed");
10945
9816
  }
@@ -10961,11 +9832,11 @@ async function previewCommand() {
10961
9832
  const globalsContent = readFileSync15(globalsPath, "utf-8");
10962
9833
  const cssIssues = validateV4GlobalsCss(globalsContent);
10963
9834
  if (cssIssues.length > 0) {
10964
- console.log(chalk15.yellow("\n\u26A0\uFE0F globals.css validation warnings:"));
9835
+ console.log(chalk14.yellow("\n\u26A0\uFE0F globals.css validation warnings:"));
10965
9836
  for (const issue of cssIssues) {
10966
- console.log(chalk15.yellow(` \u2022 ${issue}`));
9837
+ console.log(chalk14.yellow(` \u2022 ${issue}`));
10967
9838
  }
10968
- console.log(chalk15.dim(' Run "coherent chat" to regenerate globals.css\n'));
9839
+ console.log(chalk14.dim(' Run "coherent chat" to regenerate globals.css\n'));
10969
9840
  }
10970
9841
  }
10971
9842
  }
@@ -10976,27 +9847,27 @@ async function previewCommand() {
10976
9847
  await fixMissingComponentExports(projectRoot);
10977
9848
  await backfillPageAnalysis(projectRoot);
10978
9849
  spinner.succeed("Project ready");
10979
- console.log(chalk15.dim(" \u{1F4A1} Edited files manually? Run `coherent sync` to update the Design System.\n"));
10980
- console.log(chalk15.blue("\n\u{1F680} Starting Next.js dev server...\n"));
9850
+ console.log(chalk14.dim(" \u{1F4A1} Edited files manually? Run `coherent sync` to update the Design System.\n"));
9851
+ console.log(chalk14.blue("\n\u{1F680} Starting Next.js dev server...\n"));
10981
9852
  await launchWithMonitoring(projectRoot, 0);
10982
9853
  } catch (error) {
10983
9854
  spinner.fail("Failed to start dev server");
10984
9855
  if (error instanceof Error) {
10985
- console.error(chalk15.red(`
9856
+ console.error(chalk14.red(`
10986
9857
  \u274C ${error.message}`));
10987
9858
  if (error.message.includes("package.json")) {
10988
- console.log(chalk15.yellow("\n\u{1F4A1} Tip: Make sure you're in a Coherent project directory."));
10989
- console.log(chalk15.dim(' Run "coherent init" to create a new project.'));
9859
+ console.log(chalk14.yellow("\n\u{1F4A1} Tip: Make sure you're in a Coherent project directory."));
9860
+ console.log(chalk14.dim(' Run "coherent init" to create a new project.'));
10990
9861
  }
10991
9862
  } else {
10992
- console.error(chalk15.red("Unknown error occurred"));
9863
+ console.error(chalk14.red("Unknown error occurred"));
10993
9864
  }
10994
9865
  process.exit(1);
10995
9866
  }
10996
9867
  }
10997
9868
 
10998
9869
  // src/commands/export.ts
10999
- import chalk16 from "chalk";
9870
+ import chalk15 from "chalk";
11000
9871
  import ora4 from "ora";
11001
9872
  import { spawn as spawn2 } from "child_process";
11002
9873
  import { existsSync as existsSync21, rmSync as rmSync4, readdirSync as readdirSync7 } from "fs";
@@ -11275,7 +10146,7 @@ async function exportCommand(options = {}) {
11275
10146
  const missingDeps = await findMissingDepsInExport(outputDir);
11276
10147
  if (missingDeps.length > 0) {
11277
10148
  console.log(
11278
- chalk16.yellow(
10149
+ chalk15.yellow(
11279
10150
  "\n\u26A0\uFE0F Warning: exported code imports packages not in package.json: " + missingDeps.join(", ") + "\n Add them to dependencies and run npm install in the export dir.\n"
11280
10151
  )
11281
10152
  );
@@ -11291,7 +10162,7 @@ async function exportCommand(options = {}) {
11291
10162
  spinner.succeed("Dependencies installed");
11292
10163
  await ensureReadmeDeploySection(outputDir);
11293
10164
  await patchNextConfigForExport(outputDir);
11294
- console.log(chalk16.dim("\n Tip: run `coherent check` before export to catch quality issues.\n"));
10165
+ console.log(chalk15.dim("\n Tip: run `coherent check` before export to catch quality issues.\n"));
11295
10166
  let buildOk = false;
11296
10167
  if (doBuild) {
11297
10168
  spinner.start("Running next build...");
@@ -11301,7 +10172,7 @@ async function exportCommand(options = {}) {
11301
10172
  spinner.succeed("Build: success");
11302
10173
  } catch (e) {
11303
10174
  spinner.fail("Build failed");
11304
- if (e instanceof Error) console.error(chalk16.red(e.message));
10175
+ if (e instanceof Error) console.error(chalk15.red(e.message));
11305
10176
  }
11306
10177
  } else {
11307
10178
  buildOk = true;
@@ -11309,23 +10180,23 @@ async function exportCommand(options = {}) {
11309
10180
  const pageCount = await countPages(outputDir);
11310
10181
  const componentCount = countComponents(outputDir);
11311
10182
  spinner.stop();
11312
- console.log(chalk16.green("\n\u2705 Exported to " + outputDir + "\n"));
11313
- console.log(chalk16.blue(" Pages: " + pageCount));
11314
- console.log(chalk16.blue(" Components: " + componentCount + " (base + shared)"));
11315
- console.log(chalk16.blue(" Build: " + (doBuild ? buildOk ? "success" : "failed" : "skipped (--no-build)")));
10183
+ console.log(chalk15.green("\n\u2705 Exported to " + outputDir + "\n"));
10184
+ console.log(chalk15.blue(" Pages: " + pageCount));
10185
+ console.log(chalk15.blue(" Components: " + componentCount + " (base + shared)"));
10186
+ console.log(chalk15.blue(" Build: " + (doBuild ? buildOk ? "success" : "failed" : "skipped (--no-build)")));
11316
10187
  console.log("");
11317
- console.log(chalk16.dim(" Deploy: npx vercel " + outputDir));
11318
- console.log(chalk16.dim(" or: npx netlify deploy --dir " + outputDir + "/.next"));
10188
+ console.log(chalk15.dim(" Deploy: npx vercel " + outputDir));
10189
+ console.log(chalk15.dim(" or: npx netlify deploy --dir " + outputDir + "/.next"));
11319
10190
  console.log("");
11320
10191
  } catch (error) {
11321
10192
  spinner.fail("Export failed");
11322
- if (error instanceof Error) console.error(chalk16.red("\n\u274C " + error.message));
10193
+ if (error instanceof Error) console.error(chalk15.red("\n\u274C " + error.message));
11323
10194
  process.exit(1);
11324
10195
  }
11325
10196
  }
11326
10197
 
11327
10198
  // src/commands/status.ts
11328
- import chalk17 from "chalk";
10199
+ import chalk16 from "chalk";
11329
10200
  import { basename } from "path";
11330
10201
  import { DesignSystemManager as DesignSystemManager9 } from "@getcoherent/core";
11331
10202
  function countTokens(tokens) {
@@ -11349,58 +10220,58 @@ async function statusCommand() {
11349
10220
  try {
11350
10221
  const project = findConfig();
11351
10222
  if (!project) {
11352
- console.log(chalk17.yellow("\u26A0\uFE0F Not in a Coherent project\n"));
10223
+ console.log(chalk16.yellow("\u26A0\uFE0F Not in a Coherent project\n"));
11353
10224
  console.log("Initialize a project:");
11354
- console.log(chalk17.white(" $ coherent init\n"));
10225
+ console.log(chalk16.white(" $ coherent init\n"));
11355
10226
  return;
11356
10227
  }
11357
- console.log(chalk17.cyan("\n\u2728 Current Project\n"));
11358
- console.log(chalk17.gray("\u{1F4C1} Location: ") + chalk17.white(project.root));
11359
- console.log(chalk17.gray("\u{1F4C4} Config: ") + chalk17.white(basename(project.configPath)));
10228
+ console.log(chalk16.cyan("\n\u2728 Current Project\n"));
10229
+ console.log(chalk16.gray("\u{1F4C1} Location: ") + chalk16.white(project.root));
10230
+ console.log(chalk16.gray("\u{1F4C4} Config: ") + chalk16.white(basename(project.configPath)));
11360
10231
  console.log("");
11361
10232
  try {
11362
10233
  const manager = new DesignSystemManager9(project.configPath);
11363
10234
  await manager.load();
11364
10235
  const config2 = manager.getConfig();
11365
- console.log(chalk17.cyan("\u{1F4CA} Statistics:\n"));
10236
+ console.log(chalk16.cyan("\u{1F4CA} Statistics:\n"));
11366
10237
  const pageCount = Array.isArray(config2.pages) ? config2.pages.length : Object.keys(config2.pages || {}).length;
11367
- console.log(chalk17.gray(" Pages: ") + chalk17.white(String(pageCount)));
10238
+ console.log(chalk16.gray(" Pages: ") + chalk16.white(String(pageCount)));
11368
10239
  const componentCount = Array.isArray(config2.components) ? config2.components.length : Object.keys(config2.components || {}).length;
11369
- console.log(chalk17.gray(" Components: ") + chalk17.white(String(componentCount)));
10240
+ console.log(chalk16.gray(" Components: ") + chalk16.white(String(componentCount)));
11370
10241
  const tokenCount = countTokens(config2.tokens);
11371
- console.log(chalk17.gray(" Design tokens: ") + chalk17.white(String(tokenCount)));
10242
+ console.log(chalk16.gray(" Design tokens: ") + chalk16.white(String(tokenCount)));
11372
10243
  console.log("");
11373
10244
  const recent = readRecentChanges(project.root);
11374
10245
  if (recent.length > 0) {
11375
- console.log(chalk17.cyan("\u{1F4DD} Recent changes:\n"));
10246
+ console.log(chalk16.cyan("\u{1F4DD} Recent changes:\n"));
11376
10247
  recent.slice(0, 5).forEach((change) => {
11377
10248
  const ago = formatTimeAgo(change.timestamp);
11378
- console.log(chalk17.gray(" \u2022 ") + chalk17.white(change.description) + chalk17.gray(` (${ago})`));
10249
+ console.log(chalk16.gray(" \u2022 ") + chalk16.white(change.description) + chalk16.gray(` (${ago})`));
11379
10250
  });
11380
10251
  console.log("");
11381
10252
  }
11382
- console.log(chalk17.cyan("\u{1F680} Quick actions:\n"));
11383
- console.log(chalk17.white(' $ coherent chat "add new page"'));
11384
- console.log(chalk17.white(" $ coherent preview"));
11385
- console.log(chalk17.white(" $ coherent export"));
10253
+ console.log(chalk16.cyan("\u{1F680} Quick actions:\n"));
10254
+ console.log(chalk16.white(' $ coherent chat "add new page"'));
10255
+ console.log(chalk16.white(" $ coherent preview"));
10256
+ console.log(chalk16.white(" $ coherent export"));
11386
10257
  console.log("");
11387
10258
  } catch (error) {
11388
- console.error(chalk17.red("Error loading config:"));
10259
+ console.error(chalk16.red("Error loading config:"));
11389
10260
  if (error instanceof Error) {
11390
- console.error(chalk17.red(` ${error.message}`));
10261
+ console.error(chalk16.red(` ${error.message}`));
11391
10262
  } else {
11392
- console.error(chalk17.red(" Unknown error"));
10263
+ console.error(chalk16.red(" Unknown error"));
11393
10264
  }
11394
10265
  console.log("");
11395
10266
  }
11396
10267
  } catch (error) {
11397
- console.error(chalk17.red("\u274C Command failed:"), error instanceof Error ? error.message : "Unknown error");
10268
+ console.error(chalk16.red("\u274C Command failed:"), error instanceof Error ? error.message : "Unknown error");
11398
10269
  process.exit(1);
11399
10270
  }
11400
10271
  }
11401
10272
 
11402
10273
  // src/commands/regenerate-docs.ts
11403
- import chalk18 from "chalk";
10274
+ import chalk17 from "chalk";
11404
10275
  import ora5 from "ora";
11405
10276
  import { DesignSystemManager as DesignSystemManager10 } from "@getcoherent/core";
11406
10277
  import { ProjectScaffolder as ProjectScaffolder2 } from "@getcoherent/core";
@@ -11408,9 +10279,9 @@ async function regenerateDocsCommand() {
11408
10279
  try {
11409
10280
  const project = findConfig();
11410
10281
  if (!project) {
11411
- console.log(chalk18.yellow("\u26A0\uFE0F Not in a Coherent project\n"));
10282
+ console.log(chalk17.yellow("\u26A0\uFE0F Not in a Coherent project\n"));
11412
10283
  console.log("Run this command from a project root that has design-system.config.ts");
11413
- console.log(chalk18.white(" $ coherent init # in an empty folder first\n"));
10284
+ console.log(chalk17.white(" $ coherent init # in an empty folder first\n"));
11414
10285
  process.exit(1);
11415
10286
  }
11416
10287
  const spinner = ora5("Regenerating documentation pages...").start();
@@ -11422,23 +10293,23 @@ async function regenerateDocsCommand() {
11422
10293
  await scaffolder.generateDocsPages();
11423
10294
  spinner.succeed("Documentation pages updated");
11424
10295
  console.log(
11425
- chalk18.gray(
10296
+ chalk17.gray(
11426
10297
  "\nUpdated: app/design-system/docs/ (layout, page, components, tokens, for-designers, recommendations)\n"
11427
10298
  )
11428
10299
  );
11429
10300
  } catch (err) {
11430
10301
  spinner.fail("Failed to regenerate docs");
11431
- console.error(chalk18.red(err instanceof Error ? err.message : String(err)));
10302
+ console.error(chalk17.red(err instanceof Error ? err.message : String(err)));
11432
10303
  process.exit(1);
11433
10304
  }
11434
10305
  } catch (error) {
11435
- console.error(chalk18.red("\u274C Command failed:"), error instanceof Error ? error.message : "Unknown error");
10306
+ console.error(chalk17.red("\u274C Command failed:"), error instanceof Error ? error.message : "Unknown error");
11436
10307
  process.exit(1);
11437
10308
  }
11438
10309
  }
11439
10310
 
11440
10311
  // src/commands/fix.ts
11441
- import chalk19 from "chalk";
10312
+ import chalk18 from "chalk";
11442
10313
  import { readdirSync as readdirSync8, readFileSync as readFileSync16, existsSync as existsSync22, writeFileSync as writeFileSync11, rmSync as rmSync5, mkdirSync as mkdirSync7 } from "fs";
11443
10314
  import { resolve as resolve13, join as join16 } from "path";
11444
10315
  import {
@@ -11490,31 +10361,31 @@ async function fixCommand(opts = {}) {
11490
10361
  const fixes = [];
11491
10362
  const remaining = [];
11492
10363
  if (dryRun) {
11493
- console.log(chalk19.cyan("\ncoherent fix --dry-run\n"));
10364
+ console.log(chalk18.cyan("\ncoherent fix --dry-run\n"));
11494
10365
  } else {
11495
- console.log(chalk19.cyan("\ncoherent fix\n"));
10366
+ console.log(chalk18.cyan("\ncoherent fix\n"));
11496
10367
  }
11497
10368
  if (!skipCache) {
11498
10369
  const nextDir = join16(projectRoot, ".next");
11499
10370
  if (existsSync22(nextDir)) {
11500
10371
  if (!dryRun) rmSync5(nextDir, { recursive: true, force: true });
11501
10372
  fixes.push("Cleared build cache");
11502
- console.log(chalk19.green(" \u2714 Cleared build cache"));
10373
+ console.log(chalk18.green(" \u2714 Cleared build cache"));
11503
10374
  }
11504
10375
  }
11505
10376
  const missingPkgs = await findMissingPackages(projectRoot);
11506
10377
  if (missingPkgs.length > 0) {
11507
10378
  if (dryRun) {
11508
10379
  fixes.push(`Would install packages: ${missingPkgs.join(", ")}`);
11509
- console.log(chalk19.green(` \u2714 Would install packages: ${missingPkgs.join(", ")}`));
10380
+ console.log(chalk18.green(` \u2714 Would install packages: ${missingPkgs.join(", ")}`));
11510
10381
  } else {
11511
10382
  const ok = await installPackages(projectRoot, missingPkgs);
11512
10383
  if (ok) {
11513
10384
  fixes.push(`Installed missing packages: ${missingPkgs.join(", ")}`);
11514
- console.log(chalk19.green(` \u2714 Installed missing packages: ${missingPkgs.join(", ")}`));
10385
+ console.log(chalk18.green(` \u2714 Installed missing packages: ${missingPkgs.join(", ")}`));
11515
10386
  } else {
11516
10387
  remaining.push(`Failed to install: ${missingPkgs.join(", ")}. Run: npm install ${missingPkgs.join(" ")}`);
11517
- console.log(chalk19.yellow(` \u26A0 Could not install: ${missingPkgs.join(", ")}`));
10388
+ console.log(chalk18.yellow(` \u26A0 Could not install: ${missingPkgs.join(", ")}`));
11518
10389
  }
11519
10390
  }
11520
10391
  }
@@ -11551,7 +10422,7 @@ async function fixCommand(opts = {}) {
11551
10422
  if (toInstall.length > 0) {
11552
10423
  if (dryRun) {
11553
10424
  fixes.push(`Would install components: ${toInstall.join(", ")}`);
11554
- console.log(chalk19.green(` \u2714 Would install components: ${toInstall.join(", ")}`));
10425
+ console.log(chalk18.green(` \u2714 Would install components: ${toInstall.join(", ")}`));
11555
10426
  } else {
11556
10427
  let installed = 0;
11557
10428
  for (const componentId of toInstall) {
@@ -11580,14 +10451,14 @@ async function fixCommand(opts = {}) {
11580
10451
  installed++;
11581
10452
  } catch (err) {
11582
10453
  console.log(
11583
- chalk19.yellow(` \u26A0 Failed to install ${componentId}: ${err instanceof Error ? err.message : "unknown"}`)
10454
+ chalk18.yellow(` \u26A0 Failed to install ${componentId}: ${err instanceof Error ? err.message : "unknown"}`)
11584
10455
  );
11585
10456
  }
11586
10457
  }
11587
10458
  if (installed > 0) {
11588
10459
  await dsm.save();
11589
10460
  fixes.push(`Installed missing components: ${toInstall.join(", ")}`);
11590
- console.log(chalk19.green(` \u2714 Installed missing components: ${toInstall.join(", ")}`));
10461
+ console.log(chalk18.green(` \u2714 Installed missing components: ${toInstall.join(", ")}`));
11591
10462
  }
11592
10463
  }
11593
10464
  }
@@ -11607,7 +10478,7 @@ async function fixCommand(opts = {}) {
11607
10478
  if (syntaxFixed > 0) {
11608
10479
  const verb = dryRun ? "Would fix" : "Fixed";
11609
10480
  fixes.push(`${verb} syntax in ${syntaxFixed} file(s)`);
11610
- console.log(chalk19.green(` \u2714 ${verb} syntax: ${syntaxFixed} file(s) (use client, metadata, quotes)`));
10481
+ console.log(chalk18.green(` \u2714 ${verb} syntax: ${syntaxFixed} file(s) (use client, metadata, quotes)`));
11611
10482
  }
11612
10483
  if (!skipQuality) {
11613
10484
  let qualityFixCount = 0;
@@ -11625,7 +10496,7 @@ async function fixCommand(opts = {}) {
11625
10496
  const uniqueFixes = [...new Set(qualityFixDetails)];
11626
10497
  const verb = dryRun ? "Would fix" : "Fixed";
11627
10498
  fixes.push(`${verb} quality in ${qualityFixCount} file(s)`);
11628
- console.log(chalk19.green(` \u2714 ${verb} ${uniqueFixes.length} quality issue type(s): ${uniqueFixes.join(", ")}`));
10499
+ console.log(chalk18.green(` \u2714 ${verb} ${uniqueFixes.length} quality issue type(s): ${uniqueFixes.join(", ")}`));
11629
10500
  }
11630
10501
  }
11631
10502
  let totalErrors = 0;
@@ -11670,13 +10541,13 @@ async function fixCommand(opts = {}) {
11670
10541
  if (dryRun) {
11671
10542
  fixes.push(`Would update ${o.id} path to ${newPath}`);
11672
10543
  } else {
11673
- console.log(chalk19.green(` \u2714 Updated ${o.id} (${o.name}) path \u2192 ${newPath}`));
10544
+ console.log(chalk18.green(` \u2714 Updated ${o.id} (${o.name}) path \u2192 ${newPath}`));
11674
10545
  }
11675
10546
  } else {
11676
10547
  if (dryRun) {
11677
10548
  fixes.push(`Would remove orphaned ${o.id} (${o.name})`);
11678
10549
  } else {
11679
- console.log(chalk19.green(` \u2714 Removed orphaned ${o.id} (${o.name}) \u2014 file missing`));
10550
+ console.log(chalk18.green(` \u2714 Removed orphaned ${o.id} (${o.name}) \u2014 file missing`));
11680
10551
  }
11681
10552
  }
11682
10553
  }
@@ -11689,7 +10560,7 @@ async function fixCommand(opts = {}) {
11689
10560
  entry.usedIn = fullActual;
11690
10561
  manifestModified = true;
11691
10562
  if (!dryRun) {
11692
- console.log(chalk19.green(` \u2714 Updated ${entry.id} usedIn: ${fullActual.join(", ") || "none"}`));
10563
+ console.log(chalk18.green(` \u2714 Updated ${entry.id} usedIn: ${fullActual.join(", ") || "none"}`));
11693
10564
  }
11694
10565
  }
11695
10566
  }
@@ -11704,10 +10575,12 @@ async function fixCommand(opts = {}) {
11704
10575
  file: comp.file,
11705
10576
  usedIn: comp.usedIn,
11706
10577
  description: "Auto-registered by fix",
11707
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
10578
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
10579
+ dependencies: [],
10580
+ source: "extracted"
11708
10581
  });
11709
10582
  manifest.nextId++;
11710
- console.log(chalk19.green(` \u2714 Registered ${id} (${comp.name}) from ${comp.file}`));
10583
+ console.log(chalk18.green(` \u2714 Registered ${id} (${comp.name}) from ${comp.file}`));
11711
10584
  } else {
11712
10585
  fixes.push(`Would register ${comp.name} from ${comp.file}`);
11713
10586
  }
@@ -11727,33 +10600,33 @@ async function fixCommand(opts = {}) {
11727
10600
  } catch {
11728
10601
  }
11729
10602
  if (fixes.length === 0 && totalErrors === 0 && totalWarnings === 0 && remaining.length === 0) {
11730
- console.log(chalk19.green("\n \u2705 Everything looks good \u2014 no issues found\n"));
11731
- console.log(chalk19.cyan(" Run: coherent preview\n"));
10603
+ console.log(chalk18.green("\n \u2705 Everything looks good \u2014 no issues found\n"));
10604
+ console.log(chalk18.cyan(" Run: coherent preview\n"));
11732
10605
  return;
11733
10606
  }
11734
10607
  if (fixes.length > 0) console.log("");
11735
10608
  if (totalErrors > 0 || totalWarnings > 0 || remaining.length > 0) {
11736
- console.log(chalk19.dim(" \u2500".repeat(25)));
11737
- console.log(chalk19.yellow(`
10609
+ console.log(chalk18.dim(" \u2500".repeat(25)));
10610
+ console.log(chalk18.yellow(`
11738
10611
  Remaining (need manual fix or AI):`));
11739
10612
  for (const { path: path4, report } of fileIssues) {
11740
- console.log(chalk19.dim(` \u{1F4C4} ${path4}`));
10613
+ console.log(chalk18.dim(` \u{1F4C4} ${path4}`));
11741
10614
  console.log(report);
11742
10615
  }
11743
10616
  for (const r of remaining) {
11744
- console.log(chalk19.yellow(` \u26A0 ${r}`));
10617
+ console.log(chalk18.yellow(` \u26A0 ${r}`));
11745
10618
  }
11746
10619
  console.log("");
11747
10620
  const parts = [];
11748
- if (totalErrors > 0) parts.push(chalk19.red(`\u274C ${totalErrors} error(s)`));
11749
- if (totalWarnings > 0) parts.push(chalk19.yellow(`\u26A0 ${totalWarnings} warning(s)`));
10621
+ if (totalErrors > 0) parts.push(chalk18.red(`\u274C ${totalErrors} error(s)`));
10622
+ if (totalWarnings > 0) parts.push(chalk18.yellow(`\u26A0 ${totalWarnings} warning(s)`));
11750
10623
  if (parts.length > 0) console.log(` ${parts.join(" ")}`);
11751
10624
  }
11752
- console.log(chalk19.cyan("\n Run: coherent preview\n"));
10625
+ console.log(chalk18.cyan("\n Run: coherent preview\n"));
11753
10626
  }
11754
10627
 
11755
10628
  // src/commands/check.ts
11756
- import chalk20 from "chalk";
10629
+ import chalk19 from "chalk";
11757
10630
  import { resolve as resolve14 } from "path";
11758
10631
  import { readdirSync as readdirSync9, readFileSync as readFileSync17, statSync as statSync3, existsSync as existsSync23 } from "fs";
11759
10632
  import { loadManifest as loadManifest11 } from "@getcoherent/core";
@@ -11801,7 +10674,7 @@ async function checkCommand(opts = {}) {
11801
10674
  const appDir = resolve14(projectRoot, "app");
11802
10675
  const files = findTsxFiles(appDir);
11803
10676
  result.pages.total = files.length;
11804
- if (!opts.json) console.log(chalk20.cyan("\n \u{1F4C4} Pages") + chalk20.dim(` (${files.length} scanned)
10677
+ if (!opts.json) console.log(chalk19.cyan("\n \u{1F4C4} Pages") + chalk19.dim(` (${files.length} scanned)
11805
10678
  `));
11806
10679
  const autoFixableTypes = /* @__PURE__ */ new Set([
11807
10680
  "RAW_COLOR",
@@ -11831,7 +10704,7 @@ async function checkCommand(opts = {}) {
11831
10704
  result.autoFixable += fileAutoFixable;
11832
10705
  if (filteredIssues.length === 0) {
11833
10706
  result.pages.clean++;
11834
- if (!opts.json) console.log(chalk20.green(` \u2714 ${relativePath}`) + chalk20.dim(" \u2014 clean"));
10707
+ if (!opts.json) console.log(chalk19.green(` \u2714 ${relativePath}`) + chalk19.dim(" \u2014 clean"));
11835
10708
  continue;
11836
10709
  }
11837
10710
  if (errors > 0) result.pages.withErrors++;
@@ -11844,9 +10717,9 @@ async function checkCommand(opts = {}) {
11844
10717
  });
11845
10718
  if (!opts.json) {
11846
10719
  const parts = [];
11847
- if (errors > 0) parts.push(chalk20.red(`${errors} error(s)`));
11848
- if (warnings > 0) parts.push(chalk20.yellow(`${warnings} warning(s)`));
11849
- console.log(chalk20.yellow(` \u26A0 ${relativePath}`) + chalk20.dim(` \u2014 ${parts.join(", ")}`));
10720
+ if (errors > 0) parts.push(chalk19.red(`${errors} error(s)`));
10721
+ if (warnings > 0) parts.push(chalk19.yellow(`${warnings} warning(s)`));
10722
+ console.log(chalk19.yellow(` \u26A0 ${relativePath}`) + chalk19.dim(` \u2014 ${parts.join(", ")}`));
11850
10723
  console.log(formatIssues(filteredIssues));
11851
10724
  }
11852
10725
  }
@@ -11872,15 +10745,15 @@ async function checkCommand(opts = {}) {
11872
10745
  }
11873
10746
  }
11874
10747
  if (!opts.json && result.links.broken.length > 0) {
11875
- console.log(chalk20.yellow(`
11876
- \u{1F517} Internal Links`) + chalk20.dim(` (${result.links.total} scanned)
10748
+ console.log(chalk19.yellow(`
10749
+ \u{1F517} Internal Links`) + chalk19.dim(` (${result.links.total} scanned)
11877
10750
  `));
11878
10751
  for (const b of result.links.broken) {
11879
- console.log(chalk20.red(` \u2717 ${b.file}:${b.line}`) + chalk20.dim(` \u2192 ${b.href} (route does not exist)`));
10752
+ console.log(chalk19.red(` \u2717 ${b.file}:${b.line}`) + chalk19.dim(` \u2192 ${b.href} (route does not exist)`));
11880
10753
  }
11881
10754
  } else if (!opts.json && result.links.total > 0) {
11882
- console.log(chalk20.green(`
11883
- \u{1F517} Internal Links`) + chalk20.dim(` \u2014 all ${result.links.total} links resolve \u2713`));
10755
+ console.log(chalk19.green(`
10756
+ \u{1F517} Internal Links`) + chalk19.dim(` \u2014 all ${result.links.total} links resolve \u2713`));
11884
10757
  }
11885
10758
  try {
11886
10759
  const manifest = await loadManifest11(project.root);
@@ -11889,7 +10762,7 @@ async function checkCommand(opts = {}) {
11889
10762
  const fullPath = resolve14(project.root, entry.file);
11890
10763
  if (!existsSync23(fullPath)) {
11891
10764
  result.pages.withErrors++;
11892
- if (!opts.json) console.log(chalk20.red(`
10765
+ if (!opts.json) console.log(chalk19.red(`
11893
10766
  \u2717 Missing shared component file: ${entry.id} (${entry.file})`));
11894
10767
  }
11895
10768
  }
@@ -11901,8 +10774,8 @@ async function checkCommand(opts = {}) {
11901
10774
  try {
11902
10775
  const manifest = await loadManifest11(projectRoot);
11903
10776
  if (!opts.json && manifest.shared.length > 0) {
11904
- console.log(chalk20.cyan(`
11905
- \u{1F9E9} Shared Components`) + chalk20.dim(` (${manifest.shared.length} registered)
10777
+ console.log(chalk19.cyan(`
10778
+ \u{1F9E9} Shared Components`) + chalk19.dim(` (${manifest.shared.length} registered)
11906
10779
  `));
11907
10780
  }
11908
10781
  let consistent = 0;
@@ -11916,8 +10789,8 @@ async function checkCommand(opts = {}) {
11916
10789
  if (!fileExists) {
11917
10790
  _orphaned++;
11918
10791
  if (!opts.json) {
11919
- console.log(chalk20.red(` \u274C ${entry.id} (${entry.name}) \u2014 file missing: ${entry.file}`));
11920
- console.log(chalk20.dim(` Fix: coherent fix or coherent sync`));
10792
+ console.log(chalk19.red(` \u274C ${entry.id} (${entry.name}) \u2014 file missing: ${entry.file}`));
10793
+ console.log(chalk19.dim(` Fix: coherent fix or coherent sync`));
11921
10794
  }
11922
10795
  continue;
11923
10796
  }
@@ -11928,11 +10801,11 @@ async function checkCommand(opts = {}) {
11928
10801
  _nameMismatch++;
11929
10802
  if (!opts.json) {
11930
10803
  console.log(
11931
- chalk20.yellow(
10804
+ chalk19.yellow(
11932
10805
  ` \u26A0 ${entry.id} \u2014 manifest name "${entry.name}" doesn't match export "${actualExports[0]}"`
11933
10806
  )
11934
10807
  );
11935
- console.log(chalk20.dim(` Fix: coherent sync`));
10808
+ console.log(chalk19.dim(` Fix: coherent sync`));
11936
10809
  }
11937
10810
  }
11938
10811
  } catch {
@@ -11947,35 +10820,35 @@ async function checkCommand(opts = {}) {
11947
10820
  if (totalUsage === 0) {
11948
10821
  unused++;
11949
10822
  if (!opts.json) {
11950
- console.log(chalk20.blue(` \u2139 ${entry.id} (${entry.name}) \u2014 registered but not used anywhere`));
11951
- console.log(chalk20.dim(` Remove: coherent components shared remove ${entry.id}`));
10823
+ console.log(chalk19.blue(` \u2139 ${entry.id} (${entry.name}) \u2014 registered but not used anywhere`));
10824
+ console.log(chalk19.dim(` Remove: coherent components shared remove ${entry.id}`));
11952
10825
  }
11953
10826
  } else {
11954
10827
  consistent++;
11955
10828
  const usageDesc = inLayout ? `layout + ${actualUsedIn.length} page(s)` : `${actualUsedIn.length} page(s)`;
11956
10829
  if (!opts.json) {
11957
- const staleNote = isStale ? chalk20.yellow(" [usedIn stale]") : "";
11958
- console.log(chalk20.green(` \u2714 ${entry.id} (${entry.name})`) + chalk20.dim(` \u2014 ${usageDesc}`) + staleNote);
10830
+ const staleNote = isStale ? chalk19.yellow(" [usedIn stale]") : "";
10831
+ console.log(chalk19.green(` \u2714 ${entry.id} (${entry.name})`) + chalk19.dim(` \u2014 ${usageDesc}`) + staleNote);
11959
10832
  }
11960
10833
  }
11961
10834
  }
11962
10835
  const unregistered = findUnregisteredComponents(projectRoot, manifest);
11963
10836
  if (unregistered.length > 0 && !opts.json) {
11964
- console.log(chalk20.cyan(`
10837
+ console.log(chalk19.cyan(`
11965
10838
  \u{1F4E6} Unregistered components found:`));
11966
10839
  for (const comp of unregistered) {
11967
- console.log(chalk20.blue(` \u2139 ${comp.name}`) + chalk20.dim(` \u2014 ${comp.file} (not in manifest)`));
11968
- console.log(chalk20.dim(` Register: coherent sync`));
10840
+ console.log(chalk19.blue(` \u2139 ${comp.name}`) + chalk19.dim(` \u2014 ${comp.file} (not in manifest)`));
10841
+ console.log(chalk19.dim(` Register: coherent sync`));
11969
10842
  }
11970
10843
  }
11971
10844
  const inlineDupes = findInlineDuplicates(projectRoot, manifest);
11972
10845
  if (inlineDupes.length > 0 && !opts.json) {
11973
- console.log(chalk20.cyan(`
10846
+ console.log(chalk19.cyan(`
11974
10847
  \u{1F50D} Inline duplicates:`));
11975
10848
  for (const dup of inlineDupes) {
11976
- console.log(chalk20.yellow(` \u26A0 ${dup.pageFile}`) + chalk20.dim(` has inline ${dup.componentName}`));
10849
+ console.log(chalk19.yellow(` \u26A0 ${dup.pageFile}`) + chalk19.dim(` has inline ${dup.componentName}`));
11977
10850
  console.log(
11978
- chalk20.dim(
10851
+ chalk19.dim(
11979
10852
  ` Use shared: import { ${dup.componentName} } from "@/${dup.sharedFile.replace(".tsx", "")}"`
11980
10853
  )
11981
10854
  );
@@ -11998,28 +10871,62 @@ async function checkCommand(opts = {}) {
11998
10871
  } catch {
11999
10872
  }
12000
10873
  }
10874
+ if (!skipShared) {
10875
+ try {
10876
+ const { validateReuse } = await import("./reuse-validator-HC4LZEKF.js");
10877
+ const { inferPageTypeFromRoute: inferPageTypeFromRoute2 } = await import("./design-constraints-EIP2XM7T.js");
10878
+ const manifest = await loadManifest11(projectRoot);
10879
+ const appDir = resolve14(projectRoot, "app");
10880
+ const pageFiles = existsSync23(appDir) ? findTsxFiles(appDir) : [];
10881
+ if (manifest.shared.length > 0 && pageFiles.length > 0) {
10882
+ const reuseWarnings = [];
10883
+ for (const file of pageFiles) {
10884
+ const code = readFileSync17(file, "utf-8");
10885
+ const relativePath = file.replace(projectRoot + "/", "");
10886
+ const route = "/" + relativePath.replace(/^app\//, "").replace(/\/page\.tsx$/, "").replace(/^\(.*?\)\//, "");
10887
+ const pageType = inferPageTypeFromRoute2(route);
10888
+ const warnings = validateReuse(manifest, code, pageType);
10889
+ for (const w of warnings) {
10890
+ reuseWarnings.push({ file: relativePath, message: w.message });
10891
+ }
10892
+ }
10893
+ if (reuseWarnings.length > 0 && !opts.json) {
10894
+ console.log(chalk19.yellow(`
10895
+ \u{1F504} Reuse Warnings`) + chalk19.dim(` (${reuseWarnings.length} found)
10896
+ `));
10897
+ for (const w of reuseWarnings.slice(0, 10)) {
10898
+ console.log(chalk19.yellow(` \u26A0 ${w.file}:`) + chalk19.dim(` ${w.message}`));
10899
+ }
10900
+ if (reuseWarnings.length > 10) {
10901
+ console.log(chalk19.dim(` ... and ${reuseWarnings.length - 10} more`));
10902
+ }
10903
+ }
10904
+ }
10905
+ } catch {
10906
+ }
10907
+ }
12001
10908
  if (opts.json) {
12002
10909
  console.log(JSON.stringify(result, null, 2));
12003
10910
  return;
12004
10911
  }
12005
- console.log(chalk20.dim("\n " + "\u2500".repeat(50)));
10912
+ console.log(chalk19.dim("\n " + "\u2500".repeat(50)));
12006
10913
  const summaryParts = [];
12007
10914
  if (!skipPages) {
12008
- summaryParts.push(`${chalk20.green(`${result.pages.clean} clean`)} pages`);
12009
- if (result.pages.withErrors > 0) summaryParts.push(chalk20.red(`${result.pages.withErrors} with errors`));
12010
- if (result.pages.withWarnings > 0) summaryParts.push(chalk20.yellow(`${result.pages.withWarnings} with warnings`));
10915
+ summaryParts.push(`${chalk19.green(`${result.pages.clean} clean`)} pages`);
10916
+ if (result.pages.withErrors > 0) summaryParts.push(chalk19.red(`${result.pages.withErrors} with errors`));
10917
+ if (result.pages.withWarnings > 0) summaryParts.push(chalk19.yellow(`${result.pages.withWarnings} with warnings`));
12011
10918
  }
12012
10919
  if (!skipShared && result.shared.total > 0) {
12013
10920
  summaryParts.push(`${result.shared.consistent} healthy shared`);
12014
10921
  if (result.shared.unused > 0) summaryParts.push(`${result.shared.unused} unused`);
12015
10922
  }
12016
10923
  if (result.links.broken.length > 0) {
12017
- summaryParts.push(chalk20.red(`${result.links.broken.length} broken link(s)`));
10924
+ summaryParts.push(chalk19.red(`${result.links.broken.length} broken link(s)`));
12018
10925
  }
12019
10926
  console.log(`
12020
10927
  ${summaryParts.join(" | ")}`);
12021
10928
  if (result.autoFixable > 0) {
12022
- console.log(chalk20.cyan(`
10929
+ console.log(chalk19.cyan(`
12023
10930
  Auto-fixable: ${result.autoFixable} issues. Run: coherent fix`));
12024
10931
  }
12025
10932
  console.log("");
@@ -12028,21 +10935,21 @@ async function checkCommand(opts = {}) {
12028
10935
  }
12029
10936
 
12030
10937
  // src/commands/repair.ts
12031
- import chalk21 from "chalk";
10938
+ import chalk20 from "chalk";
12032
10939
  async function repairCommand() {
12033
- console.log(chalk21.dim(" \u2139\uFE0F `coherent repair` is deprecated \u2014 use `coherent fix` instead\n"));
10940
+ console.log(chalk20.dim(" \u2139\uFE0F `coherent repair` is deprecated \u2014 use `coherent fix` instead\n"));
12034
10941
  await fixCommand();
12035
10942
  }
12036
10943
 
12037
10944
  // src/commands/doctor.ts
12038
- import chalk22 from "chalk";
10945
+ import chalk21 from "chalk";
12039
10946
  async function doctorCommand() {
12040
- console.log(chalk22.dim(" \u2139\uFE0F `coherent doctor` is deprecated \u2014 use `coherent fix` instead\n"));
10947
+ console.log(chalk21.dim(" \u2139\uFE0F `coherent doctor` is deprecated \u2014 use `coherent fix` instead\n"));
12041
10948
  await fixCommand();
12042
10949
  }
12043
10950
 
12044
10951
  // src/commands/rules.ts
12045
- import chalk23 from "chalk";
10952
+ import chalk22 from "chalk";
12046
10953
  async function rulesCommand() {
12047
10954
  try {
12048
10955
  const result = await regenerateCursorRules();
@@ -12053,31 +10960,31 @@ async function rulesCommand() {
12053
10960
  if (result.sharedCount !== void 0) parts.push(`${result.sharedCount} shared components`);
12054
10961
  if (result.tokenKeys !== void 0) parts.push(`${result.tokenKeys} design token keys`);
12055
10962
  const summary = parts.length > 0 ? ` (${parts.join(", ")})` : "";
12056
- console.log(chalk23.green(`\u2714 Updated .cursorrules and CLAUDE.md${summary}
10963
+ console.log(chalk22.green(`\u2714 Updated .cursorrules and CLAUDE.md${summary}
12057
10964
  `));
12058
10965
  } catch (error) {
12059
- console.error(chalk23.red("\u274C Command failed:"), error instanceof Error ? error.message : "Unknown error");
10966
+ console.error(chalk22.red("\u274C Command failed:"), error instanceof Error ? error.message : "Unknown error");
12060
10967
  process.exit(1);
12061
10968
  }
12062
10969
  }
12063
10970
 
12064
10971
  // src/commands/validate.ts
12065
- import chalk24 from "chalk";
10972
+ import chalk23 from "chalk";
12066
10973
  async function validateCommand() {
12067
- console.log(chalk24.dim(" \u2139\uFE0F `coherent validate` is deprecated \u2014 use `coherent check` instead\n"));
10974
+ console.log(chalk23.dim(" \u2139\uFE0F `coherent validate` is deprecated \u2014 use `coherent check` instead\n"));
12068
10975
  await checkCommand({ pages: true });
12069
10976
  }
12070
10977
 
12071
10978
  // src/commands/audit.ts
12072
- import chalk25 from "chalk";
10979
+ import chalk24 from "chalk";
12073
10980
  async function auditCommand(options) {
12074
- console.log(chalk25.dim(" \u2139\uFE0F `coherent audit` is deprecated \u2014 use `coherent check` instead\n"));
10981
+ console.log(chalk24.dim(" \u2139\uFE0F `coherent audit` is deprecated \u2014 use `coherent check` instead\n"));
12075
10982
  await checkCommand({ shared: true, json: options.json });
12076
10983
  }
12077
10984
 
12078
10985
  // src/commands/components.ts
12079
10986
  import { Command } from "commander";
12080
- import chalk26 from "chalk";
10987
+ import chalk25 from "chalk";
12081
10988
  import {
12082
10989
  DesignSystemManager as DesignSystemManager12,
12083
10990
  ComponentManager as ComponentManager7,
@@ -12128,19 +11035,27 @@ function createComponentsCommand() {
12128
11035
  console.log(JSON.stringify({ shared: manifest.shared, ui: installed2 }, null, 2));
12129
11036
  return;
12130
11037
  }
12131
- console.log(chalk26.bold("\n\u{1F4E6} Shared Components"));
11038
+ console.log(chalk25.bold("\n\u{1F4E6} Shared Components"));
12132
11039
  if (manifest.shared.length === 0) {
12133
- console.log(chalk26.gray(" None yet. Generate pages with header/footer to create them.\n"));
11040
+ console.log(chalk25.gray(" None yet. Generate pages with header/footer to create them.\n"));
12134
11041
  } else {
12135
- const order = { layout: 0, section: 1, widget: 2 };
11042
+ const order = {
11043
+ layout: 0,
11044
+ navigation: 1,
11045
+ "data-display": 2,
11046
+ form: 3,
11047
+ feedback: 4,
11048
+ section: 5,
11049
+ widget: 6
11050
+ };
12136
11051
  const sorted = [...manifest.shared].sort(
12137
11052
  (a, b) => (order[a.type] ?? 9) - (order[b.type] ?? 9) || a.name.localeCompare(b.name)
12138
11053
  );
12139
11054
  console.log("");
12140
11055
  sorted.forEach((entry) => {
12141
- const usage = entry.usedIn.length === 0 ? chalk26.gray("unused") : entry.usedIn.includes("app/layout.tsx") ? chalk26.green("all pages") : chalk26.gray(entry.usedIn.join(", "));
11056
+ const usage = entry.usedIn.length === 0 ? chalk25.gray("unused") : entry.usedIn.includes("app/layout.tsx") ? chalk25.green("all pages") : chalk25.gray(entry.usedIn.join(", "));
12142
11057
  console.log(
12143
- ` ${chalk26.cyan(entry.id.padEnd(8))} ${chalk26.white(entry.name.padEnd(18))} ${chalk26.gray(entry.type.padEnd(9))} ${usage}`
11058
+ ` ${chalk25.cyan(entry.id.padEnd(8))} ${chalk25.white(entry.name.padEnd(18))} ${chalk25.gray(entry.type.padEnd(9))} ${usage}`
12144
11059
  );
12145
11060
  });
12146
11061
  console.log("");
@@ -12149,24 +11064,24 @@ function createComponentsCommand() {
12149
11064
  const availableShadcn = listShadcnComponents();
12150
11065
  const installedIds = new Set(installed.map((c) => c.id));
12151
11066
  const notInstalled = availableShadcn.filter((id) => !installedIds.has(id));
12152
- console.log(chalk26.bold("\u{1F9E9} UI Components (shadcn)"));
11067
+ console.log(chalk25.bold("\u{1F9E9} UI Components (shadcn)"));
12153
11068
  if (installed.length === 0) {
12154
- console.log(chalk26.gray(" None installed yet.\n"));
11069
+ console.log(chalk25.gray(" None installed yet.\n"));
12155
11070
  } else {
12156
11071
  const names = installed.map((c) => c.name).sort();
12157
- console.log(chalk26.green(` Installed (${names.length}): `) + chalk26.white(names.join(", ")));
11072
+ console.log(chalk25.green(` Installed (${names.length}): `) + chalk25.white(names.join(", ")));
12158
11073
  }
12159
11074
  if (notInstalled.length > 0) {
12160
- console.log(chalk26.gray(` Available (${notInstalled.length}): `) + chalk26.gray(notInstalled.join(", ")));
11075
+ console.log(chalk25.gray(` Available (${notInstalled.length}): `) + chalk25.gray(notInstalled.join(", ")));
12161
11076
  }
12162
11077
  console.log("");
12163
- console.log(chalk26.cyan("\u{1F4A1} Commands:"));
12164
- console.log(chalk26.white(' coherent chat "add a testimonial component"'));
12165
- console.log(chalk26.white(' coherent chat --component "Header" "add a search button"'));
11078
+ console.log(chalk25.cyan("\u{1F4A1} Commands:"));
11079
+ console.log(chalk25.white(' coherent chat "add a testimonial component"'));
11080
+ console.log(chalk25.white(' coherent chat --component "Header" "add a search button"'));
12166
11081
  console.log("");
12167
11082
  });
12168
11083
  cmd.command("add <name>").description("Install a specific component").action(async (name) => {
12169
- console.log(chalk26.yellow(`
11084
+ console.log(chalk25.yellow(`
12170
11085
  \u{1F4A1} Use: coherent chat "add ${name} component"
12171
11086
  `));
12172
11087
  });
@@ -12179,25 +11094,35 @@ function createComponentsCommand() {
12179
11094
  console.log(JSON.stringify(manifest, null, 2));
12180
11095
  return;
12181
11096
  }
12182
- console.log(chalk26.bold("\n\u{1F4E6} Shared Components\n"));
11097
+ console.log(chalk25.bold("\n\u{1F4E6} Shared Components\n"));
12183
11098
  if (manifest.shared.length === 0) {
12184
- console.log(chalk26.yellow(" No shared components yet.\n"));
12185
- console.log(chalk26.gray(' Create via chat: coherent chat "add a page with header and footer"\n'));
11099
+ console.log(chalk25.yellow(" No shared components yet.\n"));
11100
+ console.log(chalk25.gray(' Create via chat: coherent chat "add a page with header and footer"\n'));
12186
11101
  return;
12187
11102
  }
12188
- const order = { layout: 0, section: 1, widget: 2 };
12189
- const sorted = [...manifest.shared].sort((a, b) => order[a.type] - order[b.type] || a.name.localeCompare(b.name));
11103
+ const order = {
11104
+ layout: 0,
11105
+ navigation: 1,
11106
+ "data-display": 2,
11107
+ form: 3,
11108
+ feedback: 4,
11109
+ section: 5,
11110
+ widget: 6
11111
+ };
11112
+ const sorted = [...manifest.shared].sort(
11113
+ (a, b) => (order[a.type] ?? 9) - (order[b.type] ?? 9) || a.name.localeCompare(b.name)
11114
+ );
12190
11115
  sorted.forEach((entry) => {
12191
- const usedIn = entry.usedIn.length === 0 ? chalk26.gray("(not used yet)") : entry.usedIn.length === 1 && entry.usedIn[0] === "app/layout.tsx" ? chalk26.gray("layout.tsx (all pages)") : chalk26.gray(`used in: ${entry.usedIn.join(", ")}`);
12192
- console.log(chalk26.cyan(` ${entry.id}`), chalk26.white(entry.name), chalk26.gray(entry.type));
11116
+ const usedIn = entry.usedIn.length === 0 ? chalk25.gray("(not used yet)") : entry.usedIn.length === 1 && entry.usedIn[0] === "app/layout.tsx" ? chalk25.gray("layout.tsx (all pages)") : chalk25.gray(`used in: ${entry.usedIn.join(", ")}`);
11117
+ console.log(chalk25.cyan(` ${entry.id}`), chalk25.white(entry.name), chalk25.gray(entry.type));
12193
11118
  if (opts.verbose) {
12194
- console.log(chalk26.gray(` file: ${entry.file}`));
12195
- if (entry.description) console.log(chalk26.gray(` ${entry.description}`));
11119
+ console.log(chalk25.gray(` file: ${entry.file}`));
11120
+ if (entry.description) console.log(chalk25.gray(` ${entry.description}`));
12196
11121
  }
12197
- console.log(chalk26.gray(` ${usedIn}`));
11122
+ console.log(chalk25.gray(` ${usedIn}`));
12198
11123
  console.log("");
12199
11124
  });
12200
- console.log(chalk26.cyan("\u{1F4A1} Modify by ID:"), chalk26.white('coherent chat "in CID-001 add a search button"\n'));
11125
+ console.log(chalk25.cyan("\u{1F4A1} Modify by ID:"), chalk25.white('coherent chat "in CID-001 add a search button"\n'));
12201
11126
  });
12202
11127
  sharedCmd.command("add <name>").description("Create a shared component (layout/section/widget) and register in manifest").option("-t, --type <type>", "Type: layout | section | widget", "layout").option("-d, --description <desc>", "Description").action(async (name, opts) => {
12203
11128
  const project = findConfig();
@@ -12209,12 +11134,12 @@ function createComponentsCommand() {
12209
11134
  description: opts.description,
12210
11135
  usedIn: type === "layout" ? ["app/layout.tsx"] : []
12211
11136
  });
12212
- console.log(chalk26.green(`
11137
+ console.log(chalk25.green(`
12213
11138
  \u2705 Created ${result.id} (${result.name}) at ${result.file}
12214
11139
  `));
12215
11140
  if (type === "layout") {
12216
11141
  const updated = await integrateSharedLayoutIntoRootLayout3(project.root);
12217
- if (updated) console.log(chalk26.cyan(" Updated app/layout.tsx to use shared layout components.\n"));
11142
+ if (updated) console.log(chalk25.cyan(" Updated app/layout.tsx to use shared layout components.\n"));
12218
11143
  }
12219
11144
  const sharedPagePath = resolve15(project.root, "app/design-system/shared/page.tsx");
12220
11145
  if (!existsSync24(sharedPagePath)) {
@@ -12224,23 +11149,23 @@ function createComponentsCommand() {
12224
11149
  const config2 = dsm.getConfig();
12225
11150
  const written = await writeDesignSystemFiles(project.root, config2, { sharedOnly: true });
12226
11151
  if (written.length > 0) {
12227
- console.log(chalk26.cyan(" Added Design System shared pages: /design-system/shared\n"));
11152
+ console.log(chalk25.cyan(" Added Design System shared pages: /design-system/shared\n"));
12228
11153
  }
12229
11154
  } catch (e) {
12230
- if (process.env.COHERENT_DEBUG === "1") console.error(chalk26.dim("DS shared pages write failed:"), e);
11155
+ if (process.env.COHERENT_DEBUG === "1") console.error(chalk25.dim("DS shared pages write failed:"), e);
12231
11156
  }
12232
11157
  }
12233
11158
  try {
12234
11159
  await writeCursorRules(project.root);
12235
11160
  } catch (e) {
12236
- if (process.env.COHERENT_DEBUG === "1") console.error(chalk26.dim("Could not update .cursorrules:"), e);
11161
+ if (process.env.COHERENT_DEBUG === "1") console.error(chalk25.dim("Could not update .cursorrules:"), e);
12237
11162
  }
12238
11163
  });
12239
11164
  return cmd;
12240
11165
  }
12241
11166
 
12242
11167
  // src/commands/import-cmd.ts
12243
- import chalk27 from "chalk";
11168
+ import chalk26 from "chalk";
12244
11169
  import ora6 from "ora";
12245
11170
  import { writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
12246
11171
  import { resolve as resolve16, join as join18, dirname as dirname9 } from "path";
@@ -12345,25 +11270,25 @@ function createImportCommand() {
12345
11270
  }
12346
11271
  async function importFigmaAction(urlOrKey, opts) {
12347
11272
  if (typeof urlOrKey !== "string" || !urlOrKey.trim()) {
12348
- console.error(chalk27.red("\n\u274C Figma URL or file key is required.\n"));
12349
- console.log(chalk27.dim(" Usage: coherent import figma <url-or-key> --token <your-token>\n"));
11273
+ console.error(chalk26.red("\n\u274C Figma URL or file key is required.\n"));
11274
+ console.log(chalk26.dim(" Usage: coherent import figma <url-or-key> --token <your-token>\n"));
12350
11275
  process.exit(1);
12351
11276
  }
12352
11277
  const token = opts.token ?? process.env.FIGMA_ACCESS_TOKEN ?? process.env.FIGMA_TOKEN;
12353
11278
  if (!token || typeof token !== "string") {
12354
- console.error(chalk27.red("\n\u274C Figma token required.\n"));
12355
- console.log(chalk27.dim(" Use: coherent import figma <url-or-key> --token <your-token>"));
12356
- console.log(chalk27.dim(" Or set FIGMA_ACCESS_TOKEN or FIGMA_TOKEN in your environment.\n"));
12357
- console.log(chalk27.dim(" Get a token: Figma \u2192 Settings \u2192 Personal access tokens.\n"));
11279
+ console.error(chalk26.red("\n\u274C Figma token required.\n"));
11280
+ console.log(chalk26.dim(" Use: coherent import figma <url-or-key> --token <your-token>"));
11281
+ console.log(chalk26.dim(" Or set FIGMA_ACCESS_TOKEN or FIGMA_TOKEN in your environment.\n"));
11282
+ console.log(chalk26.dim(" Get a token: Figma \u2192 Settings \u2192 Personal access tokens.\n"));
12358
11283
  process.exit(1);
12359
11284
  }
12360
11285
  const generatePages = opts.pages !== false;
12361
11286
  const dryRun = Boolean(opts.dryRun);
12362
11287
  const fileKey = FigmaClient.extractFileKey(urlOrKey);
12363
11288
  if (!fileKey) {
12364
- console.error(chalk27.red("\n\u274C Invalid Figma URL or file key.\n"));
12365
- console.log(chalk27.dim(" Use a URL like: https://www.figma.com/file/ABC123/MyDesign"));
12366
- console.log(chalk27.dim(" Or the file key: ABC123\n"));
11289
+ console.error(chalk26.red("\n\u274C Invalid Figma URL or file key.\n"));
11290
+ console.log(chalk26.dim(" Use a URL like: https://www.figma.com/file/ABC123/MyDesign"));
11291
+ console.log(chalk26.dim(" Or the file key: ABC123\n"));
12367
11292
  process.exit(1);
12368
11293
  }
12369
11294
  const project = findConfig();
@@ -12540,7 +11465,7 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
12540
11465
  try {
12541
11466
  await writeCursorRules(projectRoot);
12542
11467
  } catch (e) {
12543
- if (process.env.COHERENT_DEBUG === "1") console.error(chalk27.dim("Could not update .cursorrules:"), e);
11468
+ if (process.env.COHERENT_DEBUG === "1") console.error(chalk26.dim("Could not update .cursorrules:"), e);
12544
11469
  }
12545
11470
  } else {
12546
11471
  stats.filesWritten.push(DESIGN_SYSTEM_CONFIG_PATH);
@@ -12550,7 +11475,7 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
12550
11475
  } catch (err) {
12551
11476
  spinner.fail("Import failed");
12552
11477
  const message = err instanceof Error ? err.message : String(err);
12553
- console.error(chalk27.red("\n\u274C " + message + "\n"));
11478
+ console.error(chalk26.red("\n\u274C " + message + "\n"));
12554
11479
  process.exit(1);
12555
11480
  }
12556
11481
  }
@@ -12558,36 +11483,36 @@ function printReport(stats, opts) {
12558
11483
  const { dryRun, generatePages, fileName } = opts;
12559
11484
  console.log("");
12560
11485
  if (dryRun) {
12561
- console.log(chalk27.yellow("\u2550\u2550\u2550 Dry run (no files written) \u2550\u2550\u2550"));
11486
+ console.log(chalk26.yellow("\u2550\u2550\u2550 Dry run (no files written) \u2550\u2550\u2550"));
12562
11487
  console.log("");
12563
11488
  }
12564
- console.log(chalk27.green("\u2705 Figma import complete"));
11489
+ console.log(chalk26.green("\u2705 Figma import complete"));
12565
11490
  console.log("");
12566
- console.log(chalk27.cyan(" Statistics"));
12567
- console.log(chalk27.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
12568
- console.log(chalk27.blue(` File: ${fileName}`));
12569
- console.log(chalk27.blue(` Color styles: ${stats.colorStyles}`));
12570
- console.log(chalk27.blue(` Text styles: ${stats.textStyles}`));
11491
+ console.log(chalk26.cyan(" Statistics"));
11492
+ console.log(chalk26.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11493
+ console.log(chalk26.blue(` File: ${fileName}`));
11494
+ console.log(chalk26.blue(` Color styles: ${stats.colorStyles}`));
11495
+ console.log(chalk26.blue(` Text styles: ${stats.textStyles}`));
12571
11496
  console.log(
12572
- chalk27.blue(` Components: ${stats.componentsTotal} (${stats.baseCount} \u2192 base, ${stats.sharedCount} \u2192 shared)`)
11497
+ chalk26.blue(` Components: ${stats.componentsTotal} (${stats.baseCount} \u2192 base, ${stats.sharedCount} \u2192 shared)`)
12573
11498
  );
12574
- console.log(chalk27.blue(` Pages: ${stats.pagesGenerated}${!generatePages ? " (skipped by --no-pages)" : ""}`));
12575
- console.log(chalk27.blue(` design-system.config: ${stats.configUpdated ? "updated" : "\u2014"}`));
12576
- console.log(chalk27.blue(` Layout (Header/Footer): ${stats.layoutIntegrated ? "integrated" : "\u2014"}`));
12577
- console.log(chalk27.blue(` DS viewer files: ${stats.dsFilesWritten}`));
11499
+ console.log(chalk26.blue(` Pages: ${stats.pagesGenerated}${!generatePages ? " (skipped by --no-pages)" : ""}`));
11500
+ console.log(chalk26.blue(` design-system.config: ${stats.configUpdated ? "updated" : "\u2014"}`));
11501
+ console.log(chalk26.blue(` Layout (Header/Footer): ${stats.layoutIntegrated ? "integrated" : "\u2014"}`));
11502
+ console.log(chalk26.blue(` DS viewer files: ${stats.dsFilesWritten}`));
12578
11503
  console.log(
12579
- chalk27.blue(` Total files ${dryRun ? "that would be written" : "written"}: ${stats.filesWritten.length}`)
11504
+ chalk26.blue(` Total files ${dryRun ? "that would be written" : "written"}: ${stats.filesWritten.length}`)
12580
11505
  );
12581
11506
  console.log("");
12582
11507
  if (stats.filesWritten.length > 0 && stats.filesWritten.length <= 30) {
12583
- console.log(chalk27.dim(" Files:"));
12584
- stats.filesWritten.forEach((f) => console.log(chalk27.dim(` ${f}`)));
11508
+ console.log(chalk26.dim(" Files:"));
11509
+ stats.filesWritten.forEach((f) => console.log(chalk26.dim(` ${f}`)));
12585
11510
  console.log("");
12586
11511
  }
12587
11512
  }
12588
11513
 
12589
11514
  // src/commands/ds.ts
12590
- import chalk28 from "chalk";
11515
+ import chalk27 from "chalk";
12591
11516
  import ora7 from "ora";
12592
11517
  import { DesignSystemManager as DesignSystemManager14 } from "@getcoherent/core";
12593
11518
  async function dsRegenerateCommand() {
@@ -12602,16 +11527,16 @@ async function dsRegenerateCommand() {
12602
11527
  const config2 = dsm.getConfig();
12603
11528
  const written = await writeDesignSystemFiles(project.root, config2);
12604
11529
  spinner.succeed(`Regenerated ${written.length} Design System file(s)`);
12605
- console.log(chalk28.gray(" app/design-system/* and app/api/design-system/*\n"));
12606
- console.log(chalk28.cyan(" Open /design-system in the app to view.\n"));
11530
+ console.log(chalk27.gray(" app/design-system/* and app/api/design-system/*\n"));
11531
+ console.log(chalk27.cyan(" Open /design-system in the app to view.\n"));
12607
11532
  } catch (error) {
12608
- console.error(chalk28.red("\u274C Command failed:"), error instanceof Error ? error.message : "Unknown error");
11533
+ console.error(chalk27.red("\u274C Command failed:"), error instanceof Error ? error.message : "Unknown error");
12609
11534
  process.exit(1);
12610
11535
  }
12611
11536
  }
12612
11537
 
12613
11538
  // src/commands/update.ts
12614
- import chalk29 from "chalk";
11539
+ import chalk28 from "chalk";
12615
11540
  import ora8 from "ora";
12616
11541
  import { readFileSync as readFileSync18, existsSync as existsSync26 } from "fs";
12617
11542
  import { join as join19 } from "path";
@@ -12663,14 +11588,14 @@ async function updateCommand(opts) {
12663
11588
  const projectVersion = config2.coherentVersion || "0.0.0";
12664
11589
  if (compareSemver(projectVersion, CLI_VERSION4) === 0) {
12665
11590
  spinner.succeed("Project is already up to date");
12666
- console.log(chalk29.gray(` Version: v${CLI_VERSION4}
11591
+ console.log(chalk28.gray(` Version: v${CLI_VERSION4}
12667
11592
  `));
12668
11593
  return;
12669
11594
  }
12670
11595
  if (compareSemver(projectVersion, CLI_VERSION4) > 0) {
12671
11596
  spinner.warn("Project was created with a newer CLI version");
12672
- console.log(chalk29.yellow(` Project: v${projectVersion} \u2192 CLI: v${CLI_VERSION4}`));
12673
- console.log(chalk29.yellow(" Update your CLI: npm install -g @getcoherent/cli@latest\n"));
11597
+ console.log(chalk28.yellow(` Project: v${projectVersion} \u2192 CLI: v${CLI_VERSION4}`));
11598
+ console.log(chalk28.yellow(" Update your CLI: npm install -g @getcoherent/cli@latest\n"));
12674
11599
  return;
12675
11600
  }
12676
11601
  const report = {
@@ -12716,36 +11641,36 @@ async function updateCommand(opts) {
12716
11641
  }
12717
11642
  function printReport2(report) {
12718
11643
  const from = report.fromVersion ? `v${report.fromVersion}` : "unknown";
12719
- console.log(chalk29.green(`
11644
+ console.log(chalk28.green(`
12720
11645
  \u2714 Project updated: ${from} \u2192 v${report.toVersion}
12721
11646
  `));
12722
11647
  if (report.overlayFiles > 0) {
12723
- console.log(chalk29.white(` \u2714 Regenerated platform overlay (${report.overlayFiles} files)`));
11648
+ console.log(chalk28.white(` \u2714 Regenerated platform overlay (${report.overlayFiles} files)`));
12724
11649
  }
12725
11650
  if (report.migrationsApplied.length > 0) {
12726
11651
  for (const desc of report.migrationsApplied) {
12727
- console.log(chalk29.white(` \u2714 Migrated config: ${desc}`));
11652
+ console.log(chalk28.white(` \u2714 Migrated config: ${desc}`));
12728
11653
  }
12729
11654
  }
12730
11655
  if (report.rulesUpdated) {
12731
- console.log(chalk29.white(" \u2714 Updated .cursorrules and CLAUDE.md"));
11656
+ console.log(chalk28.white(" \u2714 Updated .cursorrules and CLAUDE.md"));
12732
11657
  }
12733
11658
  if (report.missingCssVars.length > 0) {
12734
11659
  console.log("");
12735
- console.log(chalk29.yellow(" \u26A0 New CSS variables available in globals.css:"));
11660
+ console.log(chalk28.yellow(" \u26A0 New CSS variables available in globals.css:"));
12736
11661
  for (const v of report.missingCssVars.slice(0, 10)) {
12737
- console.log(chalk29.gray(` ${v}`));
11662
+ console.log(chalk28.gray(` ${v}`));
12738
11663
  }
12739
11664
  if (report.missingCssVars.length > 10) {
12740
- console.log(chalk29.gray(` ... and ${report.missingCssVars.length - 10} more`));
11665
+ console.log(chalk28.gray(` ... and ${report.missingCssVars.length - 10} more`));
12741
11666
  }
12742
11667
  console.log("");
12743
- console.log(chalk29.cyan(" To add them automatically:"));
12744
- console.log(chalk29.white(" coherent update --patch-globals\n"));
11668
+ console.log(chalk28.cyan(" To add them automatically:"));
11669
+ console.log(chalk28.white(" coherent update --patch-globals\n"));
12745
11670
  }
12746
11671
  console.log("");
12747
- console.log(chalk29.dim(" Your pages and components were NOT modified."));
12748
- console.log(chalk29.dim(" Run `coherent check` to check existing pages against new rules.\n"));
11672
+ console.log(chalk28.dim(" Your pages and components were NOT modified."));
11673
+ console.log(chalk28.dim(" Run `coherent check` to check existing pages against new rules.\n"));
12749
11674
  }
12750
11675
  var EXPECTED_CSS_VARS = [
12751
11676
  "--background",
@@ -12829,7 +11754,7 @@ function patchGlobalsCss(projectRoot, missingVars) {
12829
11754
  }
12830
11755
 
12831
11756
  // src/commands/undo.ts
12832
- import chalk30 from "chalk";
11757
+ import chalk29 from "chalk";
12833
11758
  async function undoCommand(options) {
12834
11759
  try {
12835
11760
  const project = findConfig();
@@ -12838,41 +11763,41 @@ async function undoCommand(options) {
12838
11763
  const backups = listBackups(projectRoot);
12839
11764
  if (options.list) {
12840
11765
  if (backups.length === 0) {
12841
- console.log(chalk30.yellow("No backups found."));
11766
+ console.log(chalk29.yellow("No backups found."));
12842
11767
  return;
12843
11768
  }
12844
- console.log(chalk30.bold("\n\u{1F4E6} Available backups:\n"));
11769
+ console.log(chalk29.bold("\n\u{1F4E6} Available backups:\n"));
12845
11770
  for (const b of backups) {
12846
11771
  const date = new Date(b.timestamp);
12847
11772
  const timeStr = date.toLocaleString();
12848
- console.log(chalk30.white(` ${b.name}`));
12849
- console.log(chalk30.dim(` ${timeStr} \u2014 ${b.files} file(s)`));
11773
+ console.log(chalk29.white(` ${b.name}`));
11774
+ console.log(chalk29.dim(` ${timeStr} \u2014 ${b.files} file(s)`));
12850
11775
  console.log();
12851
11776
  }
12852
11777
  return;
12853
11778
  }
12854
11779
  if (backups.length === 0) {
12855
- console.log(chalk30.yellow("No backups found. Nothing to undo."));
11780
+ console.log(chalk29.yellow("No backups found. Nothing to undo."));
12856
11781
  return;
12857
11782
  }
12858
11783
  const latest = backups[0];
12859
11784
  const ok = restoreBackup(projectRoot, latest.name);
12860
11785
  if (!ok) {
12861
- console.log(chalk30.red("Failed to restore backup."));
11786
+ console.log(chalk29.red("Failed to restore backup."));
12862
11787
  return;
12863
11788
  }
12864
- console.log(chalk30.green("\n\u2705 Restored to previous state:\n"));
12865
- console.log(chalk30.dim(` Snapshot: ${new Date(latest.timestamp).toLocaleString()}`));
12866
- console.log(chalk30.dim(` Files: ${latest.files} restored`));
12867
- console.log(chalk30.cyan("\n Run: coherent preview\n"));
11789
+ console.log(chalk29.green("\n\u2705 Restored to previous state:\n"));
11790
+ console.log(chalk29.dim(` Snapshot: ${new Date(latest.timestamp).toLocaleString()}`));
11791
+ console.log(chalk29.dim(` Files: ${latest.files} restored`));
11792
+ console.log(chalk29.cyan("\n Run: coherent preview\n"));
12868
11793
  } catch (error) {
12869
- console.error(chalk30.red("\u274C Undo failed:"), error instanceof Error ? error.message : "Unknown error");
11794
+ console.error(chalk29.red("\u274C Undo failed:"), error instanceof Error ? error.message : "Unknown error");
12870
11795
  process.exit(1);
12871
11796
  }
12872
11797
  }
12873
11798
 
12874
11799
  // src/commands/sync.ts
12875
- import chalk31 from "chalk";
11800
+ import chalk30 from "chalk";
12876
11801
  import ora9 from "ora";
12877
11802
  import { existsSync as existsSync27, readFileSync as readFileSync19 } from "fs";
12878
11803
  import { join as join20, relative as relative5, dirname as dirname10 } from "path";
@@ -13102,6 +12027,18 @@ function extractReusablePatterns(code) {
13102
12027
  }
13103
12028
  return [...classCounts.entries()].filter(([, count]) => count >= 2).map(([pattern, count]) => ({ pattern, count, sample: pattern.slice(0, 80) })).sort((a, b) => b.count - a.count).slice(0, 10);
13104
12029
  }
12030
+ function mergeReusablePatternsToStylePatterns(patterns, existing) {
12031
+ const result = { ...existing };
12032
+ for (const p of patterns) {
12033
+ if (p.pattern.includes("rounded") && p.pattern.includes("border") && !result.card) {
12034
+ result.card = p.pattern;
12035
+ }
12036
+ if (p.pattern.includes("py-") && p.pattern.includes("px-") && !result.section) {
12037
+ result.section = p.pattern;
12038
+ }
12039
+ }
12040
+ return result;
12041
+ }
13105
12042
  async function syncCommand(options = {}) {
13106
12043
  const project = findConfig();
13107
12044
  if (!project) return exitNotCoherent();
@@ -13110,7 +12047,7 @@ async function syncCommand(options = {}) {
13110
12047
  const doTokens = runAll || options.tokens === true;
13111
12048
  const doComponents = runAll || options.components === true;
13112
12049
  const doPatterns = runAll || options.patterns === true;
13113
- if (dryRun) console.log(chalk31.yellow(" [dry-run] No files will be written\n"));
12050
+ if (dryRun) console.log(chalk30.yellow(" [dry-run] No files will be written\n"));
13114
12051
  const spinner = ora9("Scanning project files...").start();
13115
12052
  try {
13116
12053
  const appDir = join20(project.root, "app");
@@ -13251,84 +12188,87 @@ async function syncCommand(options = {}) {
13251
12188
  spinner.succeed("Updated .cursorrules and CLAUDE.md");
13252
12189
  }
13253
12190
  console.log("");
13254
- console.log(chalk31.green(`\u2705 Design System ${dryRun ? "analyzed" : "synced"} with actual code
12191
+ console.log(chalk30.green(`\u2705 Design System ${dryRun ? "analyzed" : "synced"} with actual code
13255
12192
  `));
13256
- console.log(chalk31.blue("\u{1F4C4} Pages:"));
12193
+ console.log(chalk30.blue("\u{1F4C4} Pages:"));
13257
12194
  for (const page of discoveredPages) {
13258
12195
  const a = analyzePageCode(page.code);
13259
12196
  const comps = Object.entries(a.componentUsage || {}).filter(([, c]) => c > 0).map(([n]) => n);
13260
- console.log(chalk31.gray(` ${page.route} \u2014 ${page.name}`));
13261
- if (comps.length > 0) console.log(chalk31.gray(` Components: ${comps.join(", ")}`));
13262
- if (a.sections?.length) console.log(chalk31.gray(` Sections: ${a.sections.map((s) => s.name).join(", ")}`));
12197
+ console.log(chalk30.gray(` ${page.route} \u2014 ${page.name}`));
12198
+ if (comps.length > 0) console.log(chalk30.gray(` Components: ${comps.join(", ")}`));
12199
+ if (a.sections?.length) console.log(chalk30.gray(` Sections: ${a.sections.map((s) => s.name).join(", ")}`));
13263
12200
  }
13264
12201
  if (doTokens && extractedTokens) {
13265
12202
  console.log("");
13266
- console.log(chalk31.blue("\u{1F3A8} Design Tokens (from globals.css):"));
12203
+ console.log(chalk30.blue("\u{1F3A8} Design Tokens (from globals.css):"));
13267
12204
  const lc = Object.keys(extractedTokens.colors.light).length;
13268
12205
  const dc = Object.keys(extractedTokens.colors.dark).length;
13269
- console.log(chalk31.gray(` Light: ${lc} variables | Dark: ${dc} variables`));
13270
- console.log(chalk31.gray(` Default mode: ${extractedTokens.defaultMode}`));
13271
- if (extractedTokens.radius) console.log(chalk31.gray(` Border radius: ${extractedTokens.radius}`));
12206
+ console.log(chalk30.gray(` Light: ${lc} variables | Dark: ${dc} variables`));
12207
+ console.log(chalk30.gray(` Default mode: ${extractedTokens.defaultMode}`));
12208
+ if (extractedTokens.radius) console.log(chalk30.gray(` Border radius: ${extractedTokens.radius}`));
13272
12209
  }
13273
12210
  if (doComponents && reconcileResult) {
13274
12211
  console.log("");
13275
- console.log(chalk31.blue("\u{1F9E9} Shared Components:"));
12212
+ console.log(chalk30.blue("\u{1F9E9} Shared Components:"));
13276
12213
  for (const r of reconcileResult.removed) {
13277
- console.log(chalk31.red(` \u{1F5D1} Removed ${r.id} (${r.name}) \u2014 ${r.reason}`));
12214
+ console.log(chalk30.red(` \u{1F5D1} Removed ${r.id} (${r.name}) \u2014 ${r.reason}`));
13278
12215
  }
13279
12216
  for (const u of reconcileResult.updated) {
13280
- console.log(chalk31.cyan(` \u{1F4DD} Updated ${u.id} ${u.field}: ${u.from} \u2192 ${u.to}`));
12217
+ console.log(chalk30.cyan(` \u{1F4DD} Updated ${u.id} ${u.field}: ${u.from} \u2192 ${u.to}`));
13281
12218
  }
13282
12219
  for (const a of reconcileResult.added) {
13283
- console.log(chalk31.green(` \u2728 Added ${a.id} (${a.name}) \u2014 ${a.file} (${a.type})`));
12220
+ console.log(chalk30.green(` \u2728 Added ${a.id} (${a.name}) \u2014 ${a.file} (${a.type})`));
13284
12221
  }
13285
12222
  for (const w of reconcileResult.warnings) {
13286
- console.log(chalk31.yellow(` \u26A0 ${w.message}`));
13287
- console.log(chalk31.dim(` ${w.suggestion}`));
12223
+ console.log(chalk30.yellow(` \u26A0 ${w.message}`));
12224
+ console.log(chalk30.dim(` ${w.suggestion}`));
13288
12225
  }
13289
12226
  if (reconcileResult.removed.length === 0 && reconcileResult.updated.length === 0 && reconcileResult.added.length === 0 && reconcileResult.warnings.length === 0) {
13290
- console.log(chalk31.gray(" All components consistent \u2713"));
12227
+ console.log(chalk30.gray(" All components consistent \u2713"));
13291
12228
  }
13292
12229
  }
13293
12230
  if (doPatterns && Object.keys(stylePatterns).length > 0) {
13294
12231
  console.log("");
13295
- console.log(chalk31.blue("\u{1F4D0} Style Patterns:"));
13296
- if (stylePatterns.card) console.log(chalk31.gray(` Cards: ${stylePatterns.card.slice(0, 80)}`));
13297
- if (stylePatterns.section) console.log(chalk31.gray(` Sections: ${stylePatterns.section}`));
13298
- if (stylePatterns.terminal) console.log(chalk31.gray(` Terminal: ${stylePatterns.terminal.slice(0, 80)}`));
13299
- if (stylePatterns.iconContainer) console.log(chalk31.gray(` Icons: ${stylePatterns.iconContainer.slice(0, 80)}`));
12232
+ console.log(chalk30.blue("\u{1F4D0} Style Patterns:"));
12233
+ if (stylePatterns.card) console.log(chalk30.gray(` Cards: ${stylePatterns.card.slice(0, 80)}`));
12234
+ if (stylePatterns.section) console.log(chalk30.gray(` Sections: ${stylePatterns.section}`));
12235
+ if (stylePatterns.terminal) console.log(chalk30.gray(` Terminal: ${stylePatterns.terminal.slice(0, 80)}`));
12236
+ if (stylePatterns.iconContainer) console.log(chalk30.gray(` Icons: ${stylePatterns.iconContainer.slice(0, 80)}`));
13300
12237
  if (stylePatterns.heroHeadline)
13301
- console.log(chalk31.gray(` Hero headline: ${stylePatterns.heroHeadline.slice(0, 80)}`));
12238
+ console.log(chalk30.gray(` Hero headline: ${stylePatterns.heroHeadline.slice(0, 80)}`));
13302
12239
  if (stylePatterns.sectionTitle)
13303
- console.log(chalk31.gray(` Section title: ${stylePatterns.sectionTitle.slice(0, 80)}`));
12240
+ console.log(chalk30.gray(` Section title: ${stylePatterns.sectionTitle.slice(0, 80)}`));
13304
12241
  }
13305
12242
  const tokenUsage = extractActualTokenUsage(allPageCode);
13306
12243
  if (tokenUsage.colors.length > 0) {
13307
12244
  console.log("");
13308
- console.log(chalk31.blue("\u{1F3F7}\uFE0F Actual token usage (from classNames):"));
12245
+ console.log(chalk30.blue("\u{1F3F7}\uFE0F Actual token usage (from classNames):"));
13309
12246
  console.log(
13310
- chalk31.gray(
12247
+ chalk30.gray(
13311
12248
  ` Colors: ${tokenUsage.colors.slice(0, 12).join(", ")}${tokenUsage.colors.length > 12 ? ` (+${tokenUsage.colors.length - 12})` : ""}`
13312
12249
  )
13313
12250
  );
13314
12251
  console.log(
13315
- chalk31.gray(
12252
+ chalk30.gray(
13316
12253
  ` Typography: ${tokenUsage.typography.slice(0, 8).join(", ")}${tokenUsage.typography.length > 8 ? ` (+${tokenUsage.typography.length - 8})` : ""}`
13317
12254
  )
13318
12255
  );
13319
- console.log(chalk31.gray(` Radius: ${tokenUsage.borderRadius.join(", ")}`));
12256
+ console.log(chalk30.gray(` Radius: ${tokenUsage.borderRadius.join(", ")}`));
13320
12257
  }
13321
12258
  const reusable = extractReusablePatterns(allPageCode);
13322
12259
  if (reusable.length > 0) {
12260
+ if (!dryRun) {
12261
+ config2.stylePatterns = mergeReusablePatternsToStylePatterns(reusable, config2.stylePatterns || {});
12262
+ }
13323
12263
  console.log("");
13324
- console.log(chalk31.blue(`\u{1F501} Repeating patterns (${reusable.length} \u2014 potential reusable components):`));
12264
+ console.log(chalk30.blue(`\u{1F501} Repeating patterns (${reusable.length} \u2014 potential reusable components):`));
13325
12265
  for (const p of reusable.slice(0, 5)) {
13326
- console.log(chalk31.gray(` \xD7${p.count}: ${p.sample}${p.sample.length < p.pattern.length ? "..." : ""}`));
12266
+ console.log(chalk30.gray(` \xD7${p.count}: ${p.sample}${p.sample.length < p.pattern.length ? "..." : ""}`));
13327
12267
  }
13328
12268
  }
13329
12269
  console.log("");
13330
12270
  if (!dryRun) {
13331
- console.log(chalk31.cyan(" Open /design-system in the app to see the updated view."));
12271
+ console.log(chalk30.cyan(" Open /design-system in the app to see the updated view."));
13332
12272
  }
13333
12273
  console.log("");
13334
12274
  } catch (err) {
@@ -13339,7 +12279,7 @@ async function syncCommand(options = {}) {
13339
12279
  }
13340
12280
 
13341
12281
  // src/commands/migrate.ts
13342
- import chalk32 from "chalk";
12282
+ import chalk31 from "chalk";
13343
12283
  import ora10 from "ora";
13344
12284
  import { existsSync as existsSync28, mkdirSync as mkdirSync8, cpSync, rmSync as rmSync6, writeFileSync as writeFileSync12, readFileSync as readFileSync20, readdirSync as readdirSync10 } from "fs";
13345
12285
  import { join as join21 } from "path";
@@ -13416,13 +12356,13 @@ async function migrateAction(options) {
13416
12356
  }
13417
12357
  const guard = guardPath(projectRoot);
13418
12358
  if (existsSync28(guard)) {
13419
- console.log(chalk32.yellow("A migration is already in progress."));
13420
- console.log(chalk32.dim("Run `coherent migrate --rollback` to undo, or delete .coherent/migration-in-progress"));
12359
+ console.log(chalk31.yellow("A migration is already in progress."));
12360
+ console.log(chalk31.dim("Run `coherent migrate --rollback` to undo, or delete .coherent/migration-in-progress"));
13421
12361
  return;
13422
12362
  }
13423
12363
  const uiDir = join21(projectRoot, "components", "ui");
13424
12364
  if (!existsSync28(uiDir)) {
13425
- console.log(chalk32.yellow("No components/ui directory found. Nothing to migrate."));
12365
+ console.log(chalk31.yellow("No components/ui directory found. Nothing to migrate."));
13426
12366
  return;
13427
12367
  }
13428
12368
  const provider = getComponentProvider();
@@ -13430,16 +12370,16 @@ async function migrateAction(options) {
13430
12370
  const files = readdirSync10(uiDir).filter((f) => f.endsWith(".tsx"));
13431
12371
  const migratable = files.map((f) => f.replace(".tsx", "")).filter((id) => managedIds.has(id));
13432
12372
  if (migratable.length === 0) {
13433
- console.log(chalk32.green("All components are already up to date."));
12373
+ console.log(chalk31.green("All components are already up to date."));
13434
12374
  return;
13435
12375
  }
13436
- console.log(chalk32.cyan(`
12376
+ console.log(chalk31.cyan(`
13437
12377
  Found ${migratable.length} component(s) to migrate:`));
13438
12378
  for (const id of migratable) {
13439
- console.log(chalk32.dim(` - ${id}`));
12379
+ console.log(chalk31.dim(` - ${id}`));
13440
12380
  }
13441
12381
  if (options.dryRun) {
13442
- console.log(chalk32.yellow("\n[dry-run] No changes applied."));
12382
+ console.log(chalk31.yellow("\n[dry-run] No changes applied."));
13443
12383
  return;
13444
12384
  }
13445
12385
  const spinner = ora10("Migrating components...").start();
@@ -13456,12 +12396,12 @@ Found ${migratable.length} component(s) to migrate:`));
13456
12396
  if (result.success) {
13457
12397
  migrated++;
13458
12398
  } else {
13459
- console.warn(chalk32.yellow(` \u26A0 Failed to migrate ${id}`));
12399
+ console.warn(chalk31.yellow(` \u26A0 Failed to migrate ${id}`));
13460
12400
  }
13461
12401
  }
13462
12402
  clearGuard(projectRoot);
13463
12403
  spinner.succeed(`Migrated ${migrated}/${migratable.length} components to real shadcn/ui`);
13464
- console.log(chalk32.dim(` Backup saved to: ${backup}`));
12404
+ console.log(chalk31.dim(` Backup saved to: ${backup}`));
13465
12405
  } catch (err) {
13466
12406
  spinner.fail("Migration failed \u2014 rolling back");
13467
12407
  rollback(projectRoot);
@@ -13473,7 +12413,7 @@ Found ${migratable.length} component(s) to migrate:`));
13473
12413
  import { existsSync as existsSync29, mkdirSync as mkdirSync9, readFileSync as readFileSync21, writeFileSync as writeFileSync13 } from "fs";
13474
12414
  import { join as join22 } from "path";
13475
12415
  import { homedir } from "os";
13476
- import chalk33 from "chalk";
12416
+ import chalk32 from "chalk";
13477
12417
  import { CLI_VERSION as CLI_VERSION5 } from "@getcoherent/core";
13478
12418
  var DEBUG5 = process.env.COHERENT_DEBUG === "1";
13479
12419
  var PACKAGE_NAME = "@getcoherent/cli";
@@ -13544,8 +12484,8 @@ async function checkForUpdates() {
13544
12484
  }
13545
12485
  function printUpdateNotice(latest) {
13546
12486
  console.log(
13547
- chalk33.yellow(`
13548
- \u2B06 Update available: v${CLI_VERSION5} \u2192 v${latest}`) + chalk33.dim(`
12487
+ chalk32.yellow(`
12488
+ \u2B06 Update available: v${CLI_VERSION5} \u2192 v${latest}`) + chalk32.dim(`
13549
12489
  Run: npm update -g ${PACKAGE_NAME}
13550
12490
  `)
13551
12491
  );
@@ -13564,7 +12504,10 @@ program.name("coherent").description(
13564
12504
  "Coherent Design Method \u2014 AI-powered design system generator\nby Sergei Kovtun \xB7 https://www.linkedin.com/in/sergeikovtun/"
13565
12505
  ).version(CLI_VERSION6);
13566
12506
  program.command("init").argument("[name]", "Project directory name (created if it does not exist)").description("Initialize a new Coherent project").action(initCommand);
13567
- program.command("chat").description("Modify design system via conversation").argument("[message]", "Modification request").option("--provider <provider>", "AI provider: claude|openai|auto", "auto").option("--component <name>", "Target a specific component by name or CID").option("--page <name>", "Target a specific page by name, id, or route").option("--token <name>", "Target a specific design token").option("-i, --interactive", "Interactive chat mode").action(chatCommand);
12507
+ program.command("chat").description("Modify design system via conversation").argument("[message]", "Modification request").option("--provider <provider>", "AI provider: claude|openai|auto", "auto").option("--component <name>", "Target a specific component by name or CID").option("--page <name>", "Target a specific page by name, id, or route").option("--token <name>", "Target a specific design token").option("--new-component <name>", "Create a new shared component with the given name").option(
12508
+ "--type <type>",
12509
+ "Component type for --new-component: layout, navigation, data-display, form, feedback, section"
12510
+ ).option("-i, --interactive", "Interactive chat mode").action(chatCommand);
13568
12511
  program.command("preview").description("Launch dev server for preview").action(previewCommand);
13569
12512
  program.command("check").description("Show all problems: page quality, shared components, internal links").option("--json", "Output as JSON").option("--pages", "Only check pages").option("--shared", "Only check shared components").action((opts) => checkCommand(opts));
13570
12513
  program.command("fix").description("Auto-fix everything: cache, deps, components, syntax, quality").option("--dry-run", "Show what would be fixed without writing").option("--no-cache", "Skip cache clearing").option("--no-quality", "Skip quality auto-fixes").action((opts) => fixCommand(opts));