@getcoherent/cli 0.6.17 → 0.6.19

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.
@@ -623,36 +623,18 @@ function replaceRawColors(classes, colorMap) {
623
623
  const n = parseInt(shade);
624
624
  const isDestructive = color === "red";
625
625
  if (prefix === "bg") {
626
- if (n >= 500 && n <= 700) {
627
- changed = true;
628
- return statePrefix + (isDestructive ? "bg-destructive" : "bg-primary");
629
- }
630
- if (n >= 100 && n <= 200) {
631
- changed = true;
632
- return statePrefix + (isDestructive ? "bg-destructive/10" : "bg-primary/10");
633
- }
634
- if (n >= 300 && n <= 400) {
635
- changed = true;
636
- return statePrefix + (isDestructive ? "bg-destructive/20" : "bg-primary/20");
637
- }
638
- if (n >= 800) {
639
- changed = true;
640
- return statePrefix + "bg-muted";
641
- }
626
+ changed = true;
627
+ if (n <= 100) return statePrefix + (isDestructive ? "bg-destructive/10" : "bg-primary/10");
628
+ if (n <= 200) return statePrefix + (isDestructive ? "bg-destructive/10" : "bg-primary/10");
629
+ if (n <= 400) return statePrefix + (isDestructive ? "bg-destructive/20" : "bg-primary/20");
630
+ if (n <= 700) return statePrefix + (isDestructive ? "bg-destructive" : "bg-primary");
631
+ return statePrefix + "bg-muted";
642
632
  }
643
633
  if (prefix === "text") {
644
- if (n >= 400 && n <= 600) {
645
- changed = true;
646
- return statePrefix + (isDestructive ? "text-destructive" : "text-primary");
647
- }
648
- if (n >= 100 && n <= 300) {
649
- changed = true;
650
- return statePrefix + "text-foreground";
651
- }
652
- if (n >= 700) {
653
- changed = true;
654
- return statePrefix + "text-foreground";
655
- }
634
+ changed = true;
635
+ if (n <= 300) return statePrefix + "text-foreground";
636
+ if (n <= 600) return statePrefix + (isDestructive ? "text-destructive" : "text-primary");
637
+ return statePrefix + "text-foreground";
656
638
  }
657
639
  if (prefix === "border" || prefix === "ring" || prefix === "outline") {
658
640
  changed = true;
@@ -675,24 +657,16 @@ function replaceRawColors(classes, colorMap) {
675
657
  }
676
658
  const n = parseInt(shade);
677
659
  if (prefix === "bg") {
678
- if (n >= 800) {
679
- changed = true;
680
- return statePrefix + "bg-background";
681
- }
682
- if (n >= 100 && n <= 300) {
683
- changed = true;
684
- return statePrefix + "bg-muted";
685
- }
660
+ changed = true;
661
+ if (n <= 300) return statePrefix + "bg-muted";
662
+ if (n <= 700) return statePrefix + "bg-muted";
663
+ return statePrefix + "bg-background";
686
664
  }
687
665
  if (prefix === "text") {
688
- if (n >= 100 && n <= 300) {
689
- changed = true;
690
- return statePrefix + "text-foreground";
691
- }
692
- if (n >= 400 && n <= 600) {
693
- changed = true;
694
- return statePrefix + "text-muted-foreground";
695
- }
666
+ changed = true;
667
+ if (n <= 300) return statePrefix + "text-foreground";
668
+ if (n <= 600) return statePrefix + "text-muted-foreground";
669
+ return statePrefix + "text-foreground";
696
670
  }
697
671
  if (prefix === "border" || prefix === "ring" || prefix === "outline") {
698
672
  changed = true;
@@ -969,15 +943,47 @@ ${selectImport}`
969
943
  newImport = newImport.replace(new RegExp(`\\b${dup}\\b,?\\s*`), "");
970
944
  fixes.push(`removed ${dup} from lucide import (conflicts with UI component import)`);
971
945
  }
946
+ const ICON_ALIASES = {
947
+ Github: "ExternalLink",
948
+ GitHub: "ExternalLink",
949
+ Twitter: "MessageCircle",
950
+ Linkedin: "Link2",
951
+ LinkedIn: "Link2",
952
+ Slack: "MessageSquare",
953
+ Discord: "MessageCircle",
954
+ Facebook: "Globe",
955
+ Instagram: "Camera",
956
+ YouTube: "Play",
957
+ Youtube: "Play",
958
+ TikTok: "Music",
959
+ Reddit: "MessageSquare",
960
+ Twitch: "Tv",
961
+ Figma: "Pen",
962
+ Dribbble: "Palette",
963
+ Medium: "FileText",
964
+ WhatsApp: "Phone",
965
+ Telegram: "Send",
966
+ Pinterest: "Pin",
967
+ Spotify: "Music"
968
+ };
972
969
  const invalid = iconNames.filter((name) => !lucideExports.has(name) && !nonLucideImports.has(name));
973
970
  if (invalid.length > 0) {
974
- const fallback = "Circle";
971
+ const replacements = [];
975
972
  for (const bad of invalid) {
976
- const re = new RegExp(`\\b${bad}\\b`, "g");
977
- newImport = newImport.replace(re, fallback);
978
- fixed = fixed.replace(re, fallback);
973
+ const replacement = ICON_ALIASES[bad] || "Circle";
974
+ if (lucideExports.has(replacement)) {
975
+ const re = new RegExp(`\\b${bad}\\b`, "g");
976
+ newImport = newImport.replace(re, replacement);
977
+ fixed = fixed.replace(re, replacement);
978
+ replacements.push(`${bad}\u2192${replacement}`);
979
+ } else {
980
+ const re = new RegExp(`\\b${bad}\\b`, "g");
981
+ newImport = newImport.replace(re, "Circle");
982
+ fixed = fixed.replace(re, "Circle");
983
+ replacements.push(`${bad}\u2192Circle`);
984
+ }
979
985
  }
980
- fixes.push(`invalid lucide icons \u2192 ${fallback}: ${invalid.join(", ")}`);
986
+ fixes.push(`invalid lucide icons: ${replacements.join(", ")}`);
981
987
  }
982
988
  if (duplicates.length > 0 || invalid.length > 0) {
983
989
  const importedNames = [
@@ -1151,6 +1157,27 @@ ${selectImport}`
1151
1157
  if (fixed !== beforeImgFix) {
1152
1158
  fixes.push("placeholder images \u2192 working URLs (picsum/pravatar)");
1153
1159
  }
1160
+ const beforePlaceholder = fixed;
1161
+ fixed = fixed.replace(
1162
+ />(\s*)Lorem ipsum[^<]*/gi,
1163
+ ">$1Streamline your workflow with intelligent automation and real-time collaboration tools"
1164
+ );
1165
+ fixed = fixed.replace(/>(\s*)Card content(\s*)</gi, ">$1View details$2<");
1166
+ fixed = fixed.replace(/>(\s*)Your (?:text|content) here(\s*)</gi, ">$1Get started today$2<");
1167
+ fixed = fixed.replace(/>(\s*)Description(\s*)</g, ">$1Overview$2<");
1168
+ fixed = fixed.replace(/>\s*Title\s*</g, ">Page Title<");
1169
+ fixed = fixed.replace(/placeholder\s*text/gi, "contextual content");
1170
+ fixed = fixed.replace(/"John Doe"/g, '"Alex Thompson"');
1171
+ fixed = fixed.replace(/'John Doe'/g, "'Alex Thompson'");
1172
+ fixed = fixed.replace(/"Jane Doe"/g, '"Sarah Chen"');
1173
+ fixed = fixed.replace(/'Jane Doe'/g, "'Sarah Chen'");
1174
+ fixed = fixed.replace(/"user@example\.com"/g, '"alex@company.com"');
1175
+ fixed = fixed.replace(/'user@example\.com'/g, "'alex@company.com'");
1176
+ fixed = fixed.replace(/"test@example\.com"/g, '"team@company.com"');
1177
+ fixed = fixed.replace(/'test@example\.com'/g, "'team@company.com'");
1178
+ if (fixed !== beforePlaceholder) {
1179
+ fixes.push("placeholder content \u2192 contextual content");
1180
+ }
1154
1181
  return { code: fixed, fixes };
1155
1182
  }
1156
1183
  function formatIssues(issues) {
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  formatIssues,
8
8
  validatePageQuality,
9
9
  verifyIncrementalEdit
10
- } from "./chunk-GIP36DZD.js";
10
+ } from "./chunk-CF4377B7.js";
11
11
  import {
12
12
  generateArchitecturePlan,
13
13
  getPageGroup,
@@ -6012,8 +6012,29 @@ function buildTieredComponentsPrompt(manifest, pageType) {
6012
6012
  );
6013
6013
  return sections.join("\n");
6014
6014
  }
6015
- function formatPlanSummary(plan) {
6015
+ function formatPlanSummary(plan, forRoute) {
6016
6016
  if (plan.groups.length === 0) return "";
6017
+ if (forRoute) {
6018
+ const group = plan.groups.find((g) => g.pages.includes(forRoute));
6019
+ if (!group) return "";
6020
+ const groupLine = ` Group "${group.id}" (layout: ${group.layout}): ${group.pages.join(", ")}`;
6021
+ const relevantComps = plan.sharedComponents.filter((c) => c.usedBy.includes(forRoute));
6022
+ const parts2 = [`ARCHITECTURE PLAN:
6023
+ Your group:
6024
+ ${groupLine}`];
6025
+ if (relevantComps.length > 0) {
6026
+ parts2.push(
6027
+ `Shared Components for this page:
6028
+ ${relevantComps.map((c) => ` ${c.name} (${c.type}) \u2014 ${c.description}`).join("\n")}`
6029
+ );
6030
+ }
6031
+ const routeKey = forRoute.replace(/^\//, "");
6032
+ const note = plan.pageNotes?.[routeKey];
6033
+ if (note?.sections && note.sections.length > 0) {
6034
+ parts2.push(`Page Sections: ${note.sections.join(", ")}`);
6035
+ }
6036
+ return parts2.join("\n");
6037
+ }
6017
6038
  const groupLines = plan.groups.map((g) => ` Group "${g.id}" (layout: ${g.layout}): ${g.pages.join(", ")}`);
6018
6039
  const compLines = plan.sharedComponents.map(
6019
6040
  (c) => ` ${c.name} (${c.type}) \u2014 ${c.description}; usedBy: ${c.usedBy.join(", ")}`
@@ -6045,7 +6066,7 @@ function readExistingAppPageForReference(projectRoot, plan) {
6045
6066
  if (existsSync14(filePath)) {
6046
6067
  const code = readFileSync9(filePath, "utf-8");
6047
6068
  const lines = code.split("\n");
6048
- return lines.slice(0, 200).join("\n");
6069
+ return lines.slice(0, 60).join("\n");
6049
6070
  }
6050
6071
  }
6051
6072
  }
@@ -6064,7 +6085,7 @@ function readExistingAppPageForReference(projectRoot, plan) {
6064
6085
  if (existsSync14(pagePath)) {
6065
6086
  const code = readFileSync9(pagePath, "utf-8");
6066
6087
  const lines = code.split("\n");
6067
- return lines.slice(0, 200).join("\n");
6088
+ return lines.slice(0, 60).join("\n");
6068
6089
  }
6069
6090
  }
6070
6091
  }
@@ -6321,7 +6342,6 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
6321
6342
  const currentManifest = projectRoot ? await loadManifest5(projectRoot) : null;
6322
6343
  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="#".`;
6323
6344
  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.';
6324
- const planSummaryNote = plan ? formatPlanSummary(plan) : "";
6325
6345
  const existingAppPageCode = readExistingAppPageForReference(parseOpts?.projectRoot ?? null, plan);
6326
6346
  const existingAppPageNote = existingAppPageCode ? `
6327
6347
  EXISTING APP PAGE (match these UI patterns for consistency):
@@ -6398,7 +6418,7 @@ ${existingAppPageCode}
6398
6418
  routeNote,
6399
6419
  alignmentNote,
6400
6420
  authNote,
6401
- planSummaryNote,
6421
+ plan ? formatPlanSummary(plan, route) : "",
6402
6422
  pageType !== "auth" ? existingAppPageNote : void 0,
6403
6423
  existingPagesContext,
6404
6424
  styleContext
@@ -6407,7 +6427,7 @@ ${existingAppPageCode}
6407
6427
  const result = await parseModification(prompt, modCtx, provider, parseOpts);
6408
6428
  phase5Done++;
6409
6429
  spinner.text = `Phase 5/6 \u2014 ${phase5Done}/${remainingPages.length} pages generated...`;
6410
- let codePage = result.requests.find((r) => r.type === "add-page");
6430
+ const codePage = result.requests.find((r) => r.type === "add-page");
6411
6431
  if (currentReusePlan && currentReusePlan.reuse.length > 0 && codePage) {
6412
6432
  const pageCode = codePage.changes?.pageCode;
6413
6433
  if (pageCode) {
@@ -6417,17 +6437,28 @@ ${existingAppPageCode}
6417
6437
  chalk8.dim(` \u2713 Reuse verified for "${name}": ${verification.passed.map((p) => p.component).join(", ")}`)
6418
6438
  );
6419
6439
  }
6420
- if (verification.missed.length > 0 && verification.retryDirective) {
6421
- console.log(
6422
- chalk8.yellow(
6423
- ` \u26A0 Missed reuse in "${name}": ${verification.missed.map((m) => m.component).join(", ")} \u2014 retrying...`
6424
- )
6425
- );
6440
+ if (verification.missed.length > 0) {
6441
+ const missedNames = verification.missed.map((m) => m.component);
6442
+ console.log(chalk8.yellow(` \u26A0 Missed reuse in "${name}": ${missedNames.join(", ")} \u2014 patching...`));
6426
6443
  try {
6427
- const retryPrompt = [prompt, verification.retryDirective].join("\n\n");
6428
- const retryResult = await parseModification(retryPrompt, modCtx, provider, parseOpts);
6429
- const retryPage = retryResult.requests.find((r) => r.type === "add-page");
6430
- if (retryPage) codePage = retryPage;
6444
+ const ai = await createAIProvider(provider);
6445
+ if (ai.editPageCode) {
6446
+ const patchLines = verification.missed.map((m) => {
6447
+ const importPath = m.importPath || `@/components/shared/${m.component.replace(/([A-Z])/g, (_, c, i) => (i ? "-" : "") + c.toLowerCase()).replace(/^-/, "")}`;
6448
+ return `- Add: import { ${m.component} } from '${importPath}'
6449
+ Then find any inline implementation of ${m.component} and replace with <${m.component} />`;
6450
+ });
6451
+ const patchInstruction = `Add these shared components to this page:
6452
+ ${patchLines.join("\n")}
6453
+
6454
+ Keep all existing functionality. Only add imports and replace inline duplicates.`;
6455
+ const patched = await ai.editPageCode(pageCode, patchInstruction, name);
6456
+ if (patched && patched.length > 100 && /export\s+(default\s+)?function/.test(patched)) {
6457
+ ;
6458
+ codePage.changes.pageCode = patched;
6459
+ console.log(chalk8.dim(` \u2713 Patched ${missedNames.join(", ")} into "${name}"`));
6460
+ }
6461
+ }
6431
6462
  } catch {
6432
6463
  }
6433
6464
  }
@@ -8472,7 +8503,7 @@ async function chatCommand(message, options) {
8472
8503
  spinner.start(`Creating shared component: ${componentName}...`);
8473
8504
  const { createAIProvider: createAIProvider2 } = await import("./ai-provider-CGSIYFZT.js");
8474
8505
  const { generateSharedComponent: generateSharedComponent7 } = await import("@getcoherent/core");
8475
- const { autoFixCode: autoFixCode2 } = await import("./quality-validator-MMAZYG6O.js");
8506
+ const { autoFixCode: autoFixCode2 } = await import("./quality-validator-BX3T5X6Z.js");
8476
8507
  const { extractPropsInterface, extractDependencies } = await import("./component-extractor-VYJLT5NR.js");
8477
8508
  const aiProvider = await createAIProvider2(provider ?? "auto");
8478
8509
  const prompt = `Generate a React component called "${componentName}". Description: ${message}.
@@ -4,7 +4,7 @@ import {
4
4
  formatIssues,
5
5
  validatePageQuality,
6
6
  verifyIncrementalEdit
7
- } from "./chunk-GIP36DZD.js";
7
+ } from "./chunk-CF4377B7.js";
8
8
  import "./chunk-3RG5ZIWI.js";
9
9
  export {
10
10
  autoFixCode,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.6.17",
6
+ "version": "0.6.19",
7
7
  "description": "CLI interface for Coherent Design Method",
8
8
  "type": "module",
9
9
  "main": "./dist/index.js",