@getcoherent/cli 0.5.6 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +124 -16
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -657,7 +657,8 @@ ${out}`;
657
657
  return out;
658
658
  }
659
659
  function sanitizeMetadataStrings(code) {
660
- let out = code.replace(/(:\s*'.+)\\'(\s*)$/gm, "$1'$2");
660
+ let out = code.replace(/\\'(\s*[}\],])/g, "'$1");
661
+ out = out.replace(/(:\s*'.+)\\'(\s*)$/gm, "$1'$2");
661
662
  for (const key of ["description", "title"]) {
662
663
  const re = new RegExp(`\\b${key}:\\s*'((?:[^'\\\\]|'(?![,}]))*)'`, "gs");
663
664
  out = out.replace(re, (_, inner) => `${key}: '${inner.replace(/'/g, "\\'")}'`);
@@ -665,7 +666,9 @@ function sanitizeMetadataStrings(code) {
665
666
  return out;
666
667
  }
667
668
  function fixEscapedClosingQuotes(code) {
668
- return code.replace(/(:\s*'.+)\\'(\s*)$/gm, "$1'$2");
669
+ let out = code.replace(/\\'(\s*[}\],])/g, "'$1");
670
+ out = out.replace(/(:\s*'.+)\\'(\s*)$/gm, "$1'$2");
671
+ return out;
669
672
  }
670
673
  function fixUnescapedLtInJsx(code) {
671
674
  let out = code;
@@ -3598,6 +3601,7 @@ LINKS & INTERACTIVE STATES (consistency is critical):
3598
3601
 
3599
3602
  ICONS:
3600
3603
  - Size: ALWAYS size-4 (16px). Color: ALWAYS text-muted-foreground. Import: ALWAYS from lucide-react.
3604
+ - ALWAYS add shrink-0 to icon className to prevent flex containers from squishing them.
3601
3605
 
3602
3606
  ANTI-PATTERNS (NEVER DO):
3603
3607
  - text-base as body text \u2192 use text-sm
@@ -5530,7 +5534,8 @@ function replaceRawColors(classes, colorMap) {
5530
5534
  }
5531
5535
  if (prefix === "from" || prefix === "to" || prefix === "via") {
5532
5536
  changed = true;
5533
- if (n >= 100 && n <= 300) return statePrefix + (isDestructive ? `${prefix}-destructive/20` : `${prefix}-primary/20`);
5537
+ if (n >= 100 && n <= 300)
5538
+ return statePrefix + (isDestructive ? `${prefix}-destructive/20` : `${prefix}-primary/20`);
5534
5539
  return statePrefix + (isDestructive ? `${prefix}-destructive` : `${prefix}-primary`);
5535
5540
  }
5536
5541
  return m;
@@ -5575,6 +5580,7 @@ async function autoFixCode(code) {
5575
5580
  const fixes = [];
5576
5581
  let fixed = code;
5577
5582
  const beforeQuoteFix = fixed;
5583
+ fixed = fixed.replace(/\\'(\s*[}\],])/g, "'$1");
5578
5584
  fixed = fixed.replace(/(:\s*'.+)\\'(\s*)$/gm, "$1'$2");
5579
5585
  if (fixed !== beforeQuoteFix) {
5580
5586
  fixes.push("fixed escaped closing quotes in strings");
@@ -5885,6 +5891,23 @@ ${selectImport}`
5885
5891
  }
5886
5892
  }
5887
5893
  }
5894
+ const lucideNamesMatch = fixed.match(/import\s*\{([^}]+)\}\s*from\s*["']lucide-react["']/);
5895
+ if (lucideNamesMatch) {
5896
+ const lucideNames = new Set(
5897
+ lucideNamesMatch[1].split(",").map((s) => s.trim()).filter(Boolean)
5898
+ );
5899
+ const beforeShrinkFix = fixed;
5900
+ for (const iconName of lucideNames) {
5901
+ const iconRe = new RegExp(`(<${iconName}\\s[^>]*className=")([^"]*)(")`, "g");
5902
+ fixed = fixed.replace(iconRe, (_m, pre, classes, post) => {
5903
+ if (/\bshrink-0\b/.test(classes)) return _m;
5904
+ return `${pre}${classes} shrink-0${post}`;
5905
+ });
5906
+ }
5907
+ if (fixed !== beforeShrinkFix) {
5908
+ fixes.push("added shrink-0 to icons");
5909
+ }
5910
+ }
5888
5911
  const linkWithButtonRe = /(<Link\b[^>]*>)\s*(<Button\b(?![^>]*asChild)[^>]*>)([\s\S]*?)<\/Button>\s*<\/Link>/g;
5889
5912
  const beforeLinkFix = fixed;
5890
5913
  fixed = fixed.replace(linkWithButtonRe, (_match, linkOpen, buttonOpen, inner) => {
@@ -6778,6 +6801,10 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
6778
6801
  } catch {
6779
6802
  spinner.text = "AI plan failed \u2014 extracting pages from your request...";
6780
6803
  }
6804
+ if (modCtx.config.name === "My App") {
6805
+ const nameFromPrompt = extractAppNameFromPrompt(message);
6806
+ if (nameFromPrompt) modCtx.config.name = nameFromPrompt;
6807
+ }
6781
6808
  if (pageNames.length === 0) {
6782
6809
  pageNames = extractPageNamesFromMessage(message);
6783
6810
  }
@@ -6907,6 +6934,23 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
6907
6934
  spinner.succeed(`Phase 4/4 \u2014 Generated ${allRequests.length} pages (${withCode} with full code)`);
6908
6935
  return allRequests;
6909
6936
  }
6937
+ function extractAppNameFromPrompt(prompt) {
6938
+ const patterns = [
6939
+ /(?:called|named|app\s+name)\s+["']([^"']+)["']/i,
6940
+ /(?:called|named|app\s+name)\s+(\S+)/i,
6941
+ /\b(?:build|create|make)\s+(?:a\s+)?(\S+)\s+(?:app|platform|tool|dashboard|website|saas)/i
6942
+ ];
6943
+ for (const re of patterns) {
6944
+ const m = prompt.match(re);
6945
+ if (m && m[1] && m[1].length >= 2 && m[1].length <= 30) {
6946
+ const name = m[1].replace(/[.,;:!?]$/, "");
6947
+ const skip = /* @__PURE__ */ new Set(["a", "an", "the", "my", "our", "new", "full", "complete", "simple", "modern", "beautiful", "responsive", "fast", "cool", "great", "basic", "quick", "small", "large", "custom", "nice"]);
6948
+ if (skip.has(name.toLowerCase())) continue;
6949
+ return name.charAt(0).toUpperCase() + name.slice(1);
6950
+ }
6951
+ }
6952
+ return null;
6953
+ }
6910
6954
 
6911
6955
  // src/commands/chat/modification-handler.ts
6912
6956
  import { resolve as resolve7 } from "path";
@@ -7831,19 +7875,24 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7831
7875
  if (aiPageCode) {
7832
7876
  finalPageCode = aiPageCode;
7833
7877
  if (DEBUG2) console.log(chalk11.dim(` [pageCode] Using AI-generated pageCode (user content priority)`));
7834
- } else if (page.pageType && page.structuredContent) {
7835
- const templateFn = getTemplateForPageType(page.pageType);
7836
- if (templateFn) {
7837
- try {
7838
- const pageName = (page.name || "Page").replace(/\s+/g, "");
7839
- const opts = {
7840
- route: page.route || `/${page.id || "page"}`,
7841
- pageName
7842
- };
7843
- finalPageCode = templateFn(page.structuredContent, opts);
7844
- if (DEBUG2) console.log(chalk11.dim(` [template] Used "${page.pageType}" template (no pageCode provided)`));
7845
- } catch {
7846
- if (DEBUG2) console.log(chalk11.dim(` [template] Failed for "${page.pageType}"`));
7878
+ } else {
7879
+ const inferredType = page.pageType || inferPageType(page.route || "", page.name || "");
7880
+ if (inferredType) {
7881
+ const templateFn = getTemplateForPageType(inferredType);
7882
+ if (templateFn) {
7883
+ try {
7884
+ const pageName = (page.name || "Page").replace(/\s+/g, "");
7885
+ const opts = {
7886
+ route: page.route || `/${page.id || "page"}`,
7887
+ pageName
7888
+ };
7889
+ const content = page.structuredContent || getDefaultContent(inferredType, page.name || pageName);
7890
+ finalPageCode = templateFn(content, opts);
7891
+ if (DEBUG2)
7892
+ console.log(chalk11.dim(` [template] Used "${inferredType}" template (inferred from route/name)`));
7893
+ } catch {
7894
+ if (DEBUG2) console.log(chalk11.dim(` [template] Failed for "${inferredType}"`));
7895
+ }
7847
7896
  }
7848
7897
  }
7849
7898
  }
@@ -8215,6 +8264,65 @@ ${pagesCtx}`
8215
8264
  };
8216
8265
  }
8217
8266
  }
8267
+ function inferPageType(route, name) {
8268
+ const key = (route + " " + name).toLowerCase();
8269
+ if (/\blogin\b|\bsign.?in\b/.test(key)) return "login";
8270
+ if (/\bregister\b|\bsign.?up\b/.test(key)) return "register";
8271
+ if (/\bdashboard\b/.test(key)) return "dashboard";
8272
+ if (/\bpric(e|ing)\b/.test(key)) return "pricing";
8273
+ if (/\bfaq\b/.test(key)) return "faq";
8274
+ if (/\bcontact\b/.test(key)) return "contact";
8275
+ if (/\bblog\b/.test(key)) return "blog";
8276
+ if (/\bchangelog\b/.test(key)) return "changelog";
8277
+ if (/\babout\b/.test(key)) return "about";
8278
+ if (/\bsettings?\b/.test(key)) return "settings";
8279
+ return null;
8280
+ }
8281
+ function getDefaultContent(pageType, pageName) {
8282
+ const defaults = {
8283
+ login: {
8284
+ title: "Welcome back",
8285
+ description: "Sign in to your account to continue"
8286
+ },
8287
+ register: {
8288
+ title: "Create an account",
8289
+ description: "Get started with your free account"
8290
+ },
8291
+ dashboard: {
8292
+ title: "Dashboard",
8293
+ description: `Welcome to your ${pageName} dashboard`
8294
+ },
8295
+ pricing: {
8296
+ title: "Pricing",
8297
+ description: "Choose the plan that works for you"
8298
+ },
8299
+ faq: {
8300
+ title: "Frequently Asked Questions",
8301
+ description: "Find answers to common questions"
8302
+ },
8303
+ contact: {
8304
+ title: "Contact Us",
8305
+ description: "Get in touch with our team"
8306
+ },
8307
+ blog: {
8308
+ title: "Blog",
8309
+ description: "Latest news, updates, and insights"
8310
+ },
8311
+ changelog: {
8312
+ title: "Changelog",
8313
+ description: "What's new and improved"
8314
+ },
8315
+ about: {
8316
+ title: `About ${pageName}`,
8317
+ description: "Learn more about our mission and team"
8318
+ },
8319
+ settings: {
8320
+ title: "Settings",
8321
+ description: "Manage your account and preferences"
8322
+ }
8323
+ };
8324
+ return defaults[pageType] || { title: pageName, description: "" };
8325
+ }
8218
8326
 
8219
8327
  // src/utils/nav-snapshot.ts
8220
8328
  function takeNavSnapshot(items) {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.5.6",
6
+ "version": "0.5.8",
7
7
  "description": "CLI interface for Coherent Design Method",
8
8
  "type": "module",
9
9
  "main": "./dist/index.js",