@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.
- package/dist/index.js +124 -16
- 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(
|
|
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
|
-
|
|
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)
|
|
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
|
|
7835
|
-
const
|
|
7836
|
-
if (
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
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) {
|