@getcoherent/cli 0.5.7 → 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 +122 -15
- 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
|
|
@@ -5576,6 +5580,7 @@ async function autoFixCode(code) {
|
|
|
5576
5580
|
const fixes = [];
|
|
5577
5581
|
let fixed = code;
|
|
5578
5582
|
const beforeQuoteFix = fixed;
|
|
5583
|
+
fixed = fixed.replace(/\\'(\s*[}\],])/g, "'$1");
|
|
5579
5584
|
fixed = fixed.replace(/(:\s*'.+)\\'(\s*)$/gm, "$1'$2");
|
|
5580
5585
|
if (fixed !== beforeQuoteFix) {
|
|
5581
5586
|
fixes.push("fixed escaped closing quotes in strings");
|
|
@@ -5886,6 +5891,23 @@ ${selectImport}`
|
|
|
5886
5891
|
}
|
|
5887
5892
|
}
|
|
5888
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
|
+
}
|
|
5889
5911
|
const linkWithButtonRe = /(<Link\b[^>]*>)\s*(<Button\b(?![^>]*asChild)[^>]*>)([\s\S]*?)<\/Button>\s*<\/Link>/g;
|
|
5890
5912
|
const beforeLinkFix = fixed;
|
|
5891
5913
|
fixed = fixed.replace(linkWithButtonRe, (_match, linkOpen, buttonOpen, inner) => {
|
|
@@ -6779,6 +6801,10 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
|
|
|
6779
6801
|
} catch {
|
|
6780
6802
|
spinner.text = "AI plan failed \u2014 extracting pages from your request...";
|
|
6781
6803
|
}
|
|
6804
|
+
if (modCtx.config.name === "My App") {
|
|
6805
|
+
const nameFromPrompt = extractAppNameFromPrompt(message);
|
|
6806
|
+
if (nameFromPrompt) modCtx.config.name = nameFromPrompt;
|
|
6807
|
+
}
|
|
6782
6808
|
if (pageNames.length === 0) {
|
|
6783
6809
|
pageNames = extractPageNamesFromMessage(message);
|
|
6784
6810
|
}
|
|
@@ -6908,6 +6934,23 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
|
|
|
6908
6934
|
spinner.succeed(`Phase 4/4 \u2014 Generated ${allRequests.length} pages (${withCode} with full code)`);
|
|
6909
6935
|
return allRequests;
|
|
6910
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
|
+
}
|
|
6911
6954
|
|
|
6912
6955
|
// src/commands/chat/modification-handler.ts
|
|
6913
6956
|
import { resolve as resolve7 } from "path";
|
|
@@ -7832,19 +7875,24 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
7832
7875
|
if (aiPageCode) {
|
|
7833
7876
|
finalPageCode = aiPageCode;
|
|
7834
7877
|
if (DEBUG2) console.log(chalk11.dim(` [pageCode] Using AI-generated pageCode (user content priority)`));
|
|
7835
|
-
} else
|
|
7836
|
-
const
|
|
7837
|
-
if (
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
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
|
+
}
|
|
7848
7896
|
}
|
|
7849
7897
|
}
|
|
7850
7898
|
}
|
|
@@ -8216,6 +8264,65 @@ ${pagesCtx}`
|
|
|
8216
8264
|
};
|
|
8217
8265
|
}
|
|
8218
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
|
+
}
|
|
8219
8326
|
|
|
8220
8327
|
// src/utils/nav-snapshot.ts
|
|
8221
8328
|
function takeNavSnapshot(items) {
|