@getcoherent/cli 0.6.23 → 0.6.24

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.
@@ -703,17 +703,9 @@ async function autoFixCode(code, context) {
703
703
  };
704
704
  fixed = fixed.split("\n").map((line) => {
705
705
  let l = line;
706
- l = l.replace(/&lt;=/g, (m, offset) => isInsideAttrValue(line, offset) ? m : "<=");
707
- l = l.replace(/&gt;=/g, (m, offset) => isInsideAttrValue(line, offset) ? m : ">=");
708
- l = l.replace(/&amp;&amp;/g, (m, offset) => isInsideAttrValue(line, offset) ? m : "&&");
709
- l = l.replace(
710
- /([\w)\]])\s*&lt;\s*([\w(])/g,
711
- (m, p1, p2, offset) => isInsideAttrValue(line, offset) ? m : `${p1} < ${p2}`
712
- );
713
- l = l.replace(
714
- /([\w)\]])\s*&gt;\s*([\w(])/g,
715
- (m, p1, p2, offset) => isInsideAttrValue(line, offset) ? m : `${p1} > ${p2}`
716
- );
706
+ l = l.replace(/&lt;/g, (m, offset) => isInsideAttrValue(line, offset) ? m : "<");
707
+ l = l.replace(/&gt;/g, (m, offset) => isInsideAttrValue(line, offset) ? m : ">");
708
+ l = l.replace(/&amp;/g, (m, offset) => isInsideAttrValue(line, offset) ? m : "&");
717
709
  return l;
718
710
  }).join("\n");
719
711
  if (fixed !== beforeEntityFix) {
@@ -807,6 +799,97 @@ ${fixed}`;
807
799
  }
808
800
  fixes.push("<button> \u2192 <Button> (with import)");
809
801
  }
802
+ const compositeComponents = {
803
+ select: [
804
+ "Select",
805
+ "SelectContent",
806
+ "SelectItem",
807
+ "SelectTrigger",
808
+ "SelectValue",
809
+ "SelectGroup",
810
+ "SelectLabel",
811
+ "SelectSeparator",
812
+ "SelectScrollUpButton",
813
+ "SelectScrollDownButton"
814
+ ],
815
+ dialog: [
816
+ "Dialog",
817
+ "DialogContent",
818
+ "DialogDescription",
819
+ "DialogFooter",
820
+ "DialogHeader",
821
+ "DialogTitle",
822
+ "DialogTrigger",
823
+ "DialogClose",
824
+ "DialogOverlay",
825
+ "DialogPortal"
826
+ ],
827
+ dropdown_menu: [
828
+ "DropdownMenu",
829
+ "DropdownMenuContent",
830
+ "DropdownMenuItem",
831
+ "DropdownMenuLabel",
832
+ "DropdownMenuSeparator",
833
+ "DropdownMenuTrigger",
834
+ "DropdownMenuCheckboxItem",
835
+ "DropdownMenuGroup",
836
+ "DropdownMenuRadioGroup",
837
+ "DropdownMenuRadioItem",
838
+ "DropdownMenuShortcut",
839
+ "DropdownMenuSub",
840
+ "DropdownMenuSubContent",
841
+ "DropdownMenuSubTrigger"
842
+ ],
843
+ table: ["Table", "TableBody", "TableCaption", "TableCell", "TableFooter", "TableHead", "TableHeader", "TableRow"],
844
+ tabs: ["Tabs", "TabsContent", "TabsList", "TabsTrigger"],
845
+ card: ["Card", "CardContent", "CardDescription", "CardFooter", "CardHeader", "CardTitle"],
846
+ alert_dialog: [
847
+ "AlertDialog",
848
+ "AlertDialogAction",
849
+ "AlertDialogCancel",
850
+ "AlertDialogContent",
851
+ "AlertDialogDescription",
852
+ "AlertDialogFooter",
853
+ "AlertDialogHeader",
854
+ "AlertDialogTitle",
855
+ "AlertDialogTrigger"
856
+ ],
857
+ popover: ["Popover", "PopoverContent", "PopoverTrigger"],
858
+ command: [
859
+ "Command",
860
+ "CommandDialog",
861
+ "CommandEmpty",
862
+ "CommandGroup",
863
+ "CommandInput",
864
+ "CommandItem",
865
+ "CommandList",
866
+ "CommandSeparator",
867
+ "CommandShortcut"
868
+ ],
869
+ form: ["Form", "FormControl", "FormDescription", "FormField", "FormItem", "FormLabel", "FormMessage"]
870
+ };
871
+ const beforeSubImportFix = fixed;
872
+ for (const [uiName, allExports] of Object.entries(compositeComponents)) {
873
+ const importPath = `@/components/ui/${uiName.replace(/_/g, "-")}`;
874
+ const importRe = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*['"]${importPath.replace(/[-/]/g, "\\$&")}['"]`);
875
+ const importMatch = fixed.match(importRe);
876
+ if (!importMatch) continue;
877
+ const imported = new Set(
878
+ importMatch[1].split(",").map((s) => s.trim()).filter(Boolean)
879
+ );
880
+ const usedInCode = allExports.filter((e) => {
881
+ if (imported.has(e)) return false;
882
+ return new RegExp(`<${e}[\\s/>]`).test(fixed) || new RegExp(`</${e}>`).test(fixed);
883
+ });
884
+ if (usedInCode.length > 0) {
885
+ const merged = [...imported, ...usedInCode];
886
+ const newImport = `import { ${merged.join(", ")} } from '${importPath}'`;
887
+ fixed = fixed.replace(importRe, newImport);
888
+ }
889
+ }
890
+ if (fixed !== beforeSubImportFix) {
891
+ fixes.push("added missing sub-imports for composite components");
892
+ }
810
893
  const colorMap = {
811
894
  "bg-zinc-950": "bg-background",
812
895
  "bg-zinc-900": "bg-background",
@@ -236,6 +236,29 @@ function loadPlan(projectRoot) {
236
236
  function toKebabCase(name) {
237
237
  return name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
238
238
  }
239
+ function extractPropsInterface(code, componentName) {
240
+ const interfaceRe = new RegExp(`interface\\s+${componentName}Props\\s*\\{([^}]+)\\}`, "s");
241
+ const match = code.match(interfaceRe);
242
+ if (match) {
243
+ return match[1].split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("//")).join("; ");
244
+ }
245
+ const typeRe = new RegExp(`type\\s+${componentName}Props\\s*=\\s*\\{([^}]+)\\}`, "s");
246
+ const typeMatch = code.match(typeRe);
247
+ if (typeMatch) {
248
+ return typeMatch[1].split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("//")).join("; ");
249
+ }
250
+ return void 0;
251
+ }
252
+ function extractUsageExample(code, componentName) {
253
+ const funcMatch = code.match(new RegExp(`export function ${componentName}\\s*\\(\\{([^}]+)\\}`, "s"));
254
+ if (!funcMatch) return void 0;
255
+ const props = funcMatch[1].split(",").map((p) => p.split(":")[0].trim()).filter(Boolean);
256
+ const example = props.map((p) => {
257
+ if (p.startsWith("...")) return `${p.slice(3)}={{}}`;
258
+ return `${p}={...}`;
259
+ }).join(" ");
260
+ return `<${componentName} ${example} />`;
261
+ }
239
262
  async function generateSharedComponentsFromPlan(plan, styleContext, projectRoot, aiProvider) {
240
263
  if (plan.sharedComponents.length === 0) return [];
241
264
  const componentSpecs = plan.sharedComponents.map(
@@ -297,6 +320,8 @@ Return JSON with { requests: [{ type: "add-page", changes: { name: "ComponentNam
297
320
  }
298
321
  for (const comp of results) {
299
322
  const planned = plan.sharedComponents.find((c) => c.name === comp.name);
323
+ const propsInterface = extractPropsInterface(comp.code, comp.name);
324
+ const usageExample = extractUsageExample(comp.code, comp.name);
300
325
  await generateSharedComponent(projectRoot, {
301
326
  name: comp.name,
302
327
  type: planned?.type ?? "section",
@@ -304,7 +329,9 @@ Return JSON with { requests: [{ type: "add-page", changes: { name: "ComponentNam
304
329
  description: planned?.description,
305
330
  usedIn: planned?.usedBy ?? [],
306
331
  source: "generated",
307
- overwrite: true
332
+ overwrite: true,
333
+ propsInterface,
334
+ usageExample
308
335
  });
309
336
  }
310
337
  return results;
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  formatIssues,
8
8
  validatePageQuality,
9
9
  verifyIncrementalEdit
10
- } from "./chunk-SKQRPBPF.js";
10
+ } from "./chunk-2ZL4X4QD.js";
11
11
  import {
12
12
  generateArchitecturePlan,
13
13
  getPageGroup,
@@ -16,7 +16,7 @@ import {
16
16
  routeToKey,
17
17
  savePlan,
18
18
  updateArchitecturePlan
19
- } from "./chunk-FAV3UHFM.js";
19
+ } from "./chunk-IYLHC4RC.js";
20
20
  import {
21
21
  CORE_CONSTRAINTS,
22
22
  DESIGN_QUALITY,
@@ -6315,7 +6315,7 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
6315
6315
  if (plan && plan.sharedComponents.length > 0) {
6316
6316
  spinner.start(`Phase 4.5/6 \u2014 Generating ${plan.sharedComponents.length} shared components from plan...`);
6317
6317
  try {
6318
- const { generateSharedComponentsFromPlan } = await import("./plan-generator-5I6BPEVL.js");
6318
+ const { generateSharedComponentsFromPlan } = await import("./plan-generator-BHDEJGMY.js");
6319
6319
  const generated = await generateSharedComponentsFromPlan(
6320
6320
  plan,
6321
6321
  styleContext,
@@ -6775,7 +6775,7 @@ async function regeneratePage(pageId, config2, projectRoot) {
6775
6775
  const code = await generator.generate(page, appType);
6776
6776
  const route = page.route || "/";
6777
6777
  const isAuth = isAuthRoute(route) || isAuthRoute(page.name || page.id || "");
6778
- const { loadPlan: loadPlanForPath } = await import("./plan-generator-5I6BPEVL.js");
6778
+ const { loadPlan: loadPlanForPath } = await import("./plan-generator-BHDEJGMY.js");
6779
6779
  const planForPath = loadPlanForPath(projectRoot);
6780
6780
  const filePath = routeToFsPath(projectRoot, route, planForPath || isAuth);
6781
6781
  await mkdir3(dirname5(filePath), { recursive: true });
@@ -7091,7 +7091,17 @@ function extractImportsFrom(code, fromPath) {
7091
7091
  return [...new Set(results)];
7092
7092
  }
7093
7093
  function printPostGenerationReport(opts) {
7094
- const { action, pageTitle, filePath, code, route, postFixes = [], layoutShared = [], allShared = [] } = opts;
7094
+ const {
7095
+ action,
7096
+ pageTitle,
7097
+ filePath,
7098
+ code,
7099
+ route,
7100
+ postFixes = [],
7101
+ layoutShared = [],
7102
+ allShared = [],
7103
+ groupLayout
7104
+ } = opts;
7095
7105
  const uiComponents = extractImportsFrom(code, "@/components/ui");
7096
7106
  const sharedImportNames = extractImportsFrom(code, "@/components/shared/");
7097
7107
  const inCodeShared = allShared.filter((s) => sharedImportNames.some((n) => n === s.name));
@@ -7108,8 +7118,10 @@ function printPostGenerationReport(opts) {
7108
7118
  console.log(chalk10.dim(` Shared: ${inCodeShared.map((s) => `${s.id} (${s.name})`).join(", ")}`));
7109
7119
  }
7110
7120
  const isAuthPage = route && (/^\/(login|signin|signup|register|forgot-password|reset-password)\b/.test(route) || filePath.includes("(auth)"));
7111
- if (layoutShared.length > 0 && !isAuthPage) {
7112
- console.log(chalk10.dim(` Layout: ${layoutShared.map((l) => `${l.id} (${l.name})`).join(", ")} via layout.tsx`));
7121
+ const isSidebarPage = groupLayout === "sidebar" || filePath.includes("(app)");
7122
+ const filteredLayout = isAuthPage ? [] : isSidebarPage ? layoutShared.filter((l) => /sidebar/i.test(l.name)) : layoutShared;
7123
+ if (filteredLayout.length > 0) {
7124
+ console.log(chalk10.dim(` Layout: ${filteredLayout.map((l) => `${l.id} (${l.name})`).join(", ")} via layout.tsx`));
7113
7125
  }
7114
7126
  if (iconCount > 0) {
7115
7127
  console.log(chalk10.dim(` Icons: ${iconCount} from lucide-react`));
@@ -8534,7 +8546,7 @@ async function chatCommand(message, options) {
8534
8546
  spinner.start(`Creating shared component: ${componentName}...`);
8535
8547
  const { createAIProvider: createAIProvider2 } = await import("./ai-provider-CGSIYFZT.js");
8536
8548
  const { generateSharedComponent: generateSharedComponent7 } = await import("@getcoherent/core");
8537
- const { autoFixCode: autoFixCode2 } = await import("./quality-validator-OX25OLIR.js");
8549
+ const { autoFixCode: autoFixCode2 } = await import("./quality-validator-G5AE4337.js");
8538
8550
  const { extractPropsInterface, extractDependencies } = await import("./component-extractor-VYJLT5NR.js");
8539
8551
  const aiProvider = await createAIProvider2(provider ?? "auto");
8540
8552
  const prompt = `Generate a React component called "${componentName}". Description: ${message}.
@@ -11,7 +11,7 @@ import {
11
11
  routeToKey,
12
12
  savePlan,
13
13
  updateArchitecturePlan
14
- } from "./chunk-FAV3UHFM.js";
14
+ } from "./chunk-IYLHC4RC.js";
15
15
  import "./chunk-5AHG4NNX.js";
16
16
  import "./chunk-3RG5ZIWI.js";
17
17
  export {
@@ -4,7 +4,7 @@ import {
4
4
  formatIssues,
5
5
  validatePageQuality,
6
6
  verifyIncrementalEdit
7
- } from "./chunk-SKQRPBPF.js";
7
+ } from "./chunk-2ZL4X4QD.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.23",
6
+ "version": "0.6.24",
7
7
  "description": "CLI interface for Coherent Design Method",
8
8
  "type": "module",
9
9
  "main": "./dist/index.js",
@@ -43,7 +43,7 @@
43
43
  "ora": "^7.0.1",
44
44
  "prompts": "^2.4.2",
45
45
  "zod": "^3.22.4",
46
- "@getcoherent/core": "0.6.23"
46
+ "@getcoherent/core": "0.6.24"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/node": "^20.11.0",