@getcoherent/cli 0.5.15 → 0.6.0
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/chunk-4M2RBSYF.js +206 -0
- package/dist/index.js +879 -603
- package/dist/plan-generator-XKMZTEGK.js +29 -0
- package/package.json +10 -10
- package/LICENSE +0 -21
package/dist/index.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateArchitecturePlan,
|
|
3
|
+
getPageGroup,
|
|
4
|
+
getPageType,
|
|
5
|
+
loadPlan,
|
|
6
|
+
savePlan
|
|
7
|
+
} from "./chunk-4M2RBSYF.js";
|
|
1
8
|
import {
|
|
2
9
|
__require
|
|
3
10
|
} from "./chunk-3RG5ZIWI.js";
|
|
@@ -1984,8 +1991,8 @@ function fixEscapedClosingQuotes(code) {
|
|
|
1984
1991
|
}
|
|
1985
1992
|
function fixUnescapedLtInJsx(code) {
|
|
1986
1993
|
let out = code;
|
|
1987
|
-
out = out.replace(/>([
|
|
1988
|
-
out = out.replace(/>([
|
|
1994
|
+
out = out.replace(/>([^<\n]*)<(\d)/g, ">$1<$2");
|
|
1995
|
+
out = out.replace(/>([^<\n]*)<([^/a-zA-Z!{>\n])/g, ">$1<$2");
|
|
1989
1996
|
return out;
|
|
1990
1997
|
}
|
|
1991
1998
|
|
|
@@ -3745,7 +3752,7 @@ async function createAppRouteGroupLayout(projectPath) {
|
|
|
3745
3752
|
}
|
|
3746
3753
|
|
|
3747
3754
|
// src/commands/chat.ts
|
|
3748
|
-
import
|
|
3755
|
+
import chalk14 from "chalk";
|
|
3749
3756
|
import ora2 from "ora";
|
|
3750
3757
|
import { resolve as resolve9, relative as relative2, join as join11 } from "path";
|
|
3751
3758
|
import { existsSync as existsSync16, readFileSync as readFileSync11, mkdirSync as mkdirSync6, readdirSync as readdirSync3 } from "fs";
|
|
@@ -4101,6 +4108,7 @@ var PAGE_TEMPLATES = {
|
|
|
4101
4108
|
var AUTH_ROUTE_SEGMENTS = /* @__PURE__ */ new Set([
|
|
4102
4109
|
"login",
|
|
4103
4110
|
"signin",
|
|
4111
|
+
"sign-in",
|
|
4104
4112
|
"sign-up",
|
|
4105
4113
|
"signup",
|
|
4106
4114
|
"register",
|
|
@@ -4249,12 +4257,11 @@ CONTENT (zero placeholders):
|
|
|
4249
4257
|
- NEVER: "Lorem ipsum", "Card content", "Description here"
|
|
4250
4258
|
- ALWAYS: Real, contextual content. Realistic metric names, values, dates.
|
|
4251
4259
|
`;
|
|
4252
|
-
var
|
|
4253
|
-
## DESIGN QUALITY
|
|
4260
|
+
var DESIGN_QUALITY_COMMON = `
|
|
4261
|
+
## DESIGN QUALITY \u2014 COMMON
|
|
4254
4262
|
|
|
4255
4263
|
### Typography Hierarchy
|
|
4256
4264
|
- Page headline (h1): text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight leading-[1.1]
|
|
4257
|
-
- Landing/marketing hero headline: text-5xl md:text-6xl lg:text-7xl font-bold tracking-tight leading-[1.05]
|
|
4258
4265
|
- Section titles (h2): text-2xl md:text-3xl font-bold
|
|
4259
4266
|
- Card titles (h3): text-sm font-semibold (never text-base or text-lg)
|
|
4260
4267
|
- Body text: text-sm text-muted-foreground leading-relaxed
|
|
@@ -4267,6 +4274,29 @@ var DESIGN_QUALITY = `
|
|
|
4267
4274
|
- Sections alternate between bg-background and bg-muted/5 for rhythm
|
|
4268
4275
|
- Section dividers: border-t border-border/10 (subtle, not heavy)
|
|
4269
4276
|
|
|
4277
|
+
### Buttons with Icons
|
|
4278
|
+
- Buttons containing text + icon: ALWAYS use inline-flex items-center gap-2 whitespace-nowrap
|
|
4279
|
+
- Icon inside button: h-4 w-4 (never larger), placed AFTER text for arrows, BEFORE text for action icons
|
|
4280
|
+
- NEVER let button content wrap to multiple lines \u2014 use whitespace-nowrap on the Button component
|
|
4281
|
+
- CTA buttons: use the Button component, NEVER raw <button> or <a> styled as button
|
|
4282
|
+
|
|
4283
|
+
### Accent Color Discipline
|
|
4284
|
+
- ONE accent color per page (primary or emerald-400)
|
|
4285
|
+
- Use for: CTAs, terminal text, check icons, feature icon backgrounds, active states
|
|
4286
|
+
- NEVER mix blue + purple + emerald on same page
|
|
4287
|
+
- Badge: outline style (border-border/30 bg-transparent) not filled color
|
|
4288
|
+
- Status icons: text-emerald-400 for positive, text-red-400 for negative
|
|
4289
|
+
|
|
4290
|
+
### Dark Theme Implementation
|
|
4291
|
+
- html element: className="dark"
|
|
4292
|
+
- Background: use CSS variables from globals.css dark section
|
|
4293
|
+
- Text: text-foreground for primary, text-muted-foreground for secondary
|
|
4294
|
+
- NEVER hardcode dark colors (bg-gray-900) \u2014 always use semantic tokens
|
|
4295
|
+
- Cards and elevated elements: slightly lighter than background (bg-card or bg-zinc-900/50)
|
|
4296
|
+
`;
|
|
4297
|
+
var DESIGN_QUALITY_MARKETING = `
|
|
4298
|
+
## DESIGN QUALITY \u2014 MARKETING PAGES
|
|
4299
|
+
|
|
4270
4300
|
### Spacing Rhythm (3 distinct levels)
|
|
4271
4301
|
- Between sections: py-20 md:py-28 (generous)
|
|
4272
4302
|
- Within sections (title to content): mb-12 md:mb-16
|
|
@@ -4274,11 +4304,8 @@ var DESIGN_QUALITY = `
|
|
|
4274
4304
|
- Between cards in grid: gap-5 (tight)
|
|
4275
4305
|
- NEVER uniform spacing everywhere \u2014 contrast creates rhythm
|
|
4276
4306
|
|
|
4277
|
-
###
|
|
4278
|
-
-
|
|
4279
|
-
- Icon inside button: h-4 w-4 (never larger), placed AFTER text for arrows, BEFORE text for action icons
|
|
4280
|
-
- NEVER let button content wrap to multiple lines \u2014 use whitespace-nowrap on the Button component
|
|
4281
|
-
- CTA buttons: use the Button component, NEVER raw <button> or <a> styled as button
|
|
4307
|
+
### Hero headline
|
|
4308
|
+
- Landing/marketing hero headline: text-5xl md:text-6xl lg:text-7xl font-bold tracking-tight leading-[1.05]
|
|
4282
4309
|
|
|
4283
4310
|
### Icons in Feature Cards
|
|
4284
4311
|
- Wrap in colored container: bg-primary/10 rounded-lg p-2.5
|
|
@@ -4294,14 +4321,7 @@ var DESIGN_QUALITY = `
|
|
|
4294
4321
|
- Title bar (optional): flex with 3 dots (bg-zinc-700 rounded-full w-2.5 h-2.5) + title text-zinc-500 text-[11px]
|
|
4295
4322
|
- Copy button: text-zinc-500 hover:text-zinc-300
|
|
4296
4323
|
|
|
4297
|
-
###
|
|
4298
|
-
- ONE accent color per page (primary or emerald-400)
|
|
4299
|
-
- Use for: CTAs, terminal text, check icons, feature icon backgrounds, active states
|
|
4300
|
-
- NEVER mix blue + purple + emerald on same page
|
|
4301
|
-
- Badge: outline style (border-border/30 bg-transparent) not filled color
|
|
4302
|
-
- Status icons: text-emerald-400 for positive, text-red-400 for negative
|
|
4303
|
-
|
|
4304
|
-
### Hero Section (landing/marketing pages)
|
|
4324
|
+
### Hero Section
|
|
4305
4325
|
- Minimum height: min-h-[80vh] flex items-center justify-center
|
|
4306
4326
|
- Content: centered, max-w-3xl, flex flex-col items-center text-center gap-8
|
|
4307
4327
|
- Badge above headline: small, outline, text-xs tracking-wide
|
|
@@ -4318,8 +4338,6 @@ var DESIGN_QUALITY = `
|
|
|
4318
4338
|
### Step/Process Sections
|
|
4319
4339
|
- Numbered steps: circle with border, number inside (w-10 h-10 rounded-full border border-border/30 text-sm)
|
|
4320
4340
|
- Label above: text-xs font-semibold tracking-widest uppercase text-muted-foreground
|
|
4321
|
-
- Each step has a code block showing the command
|
|
4322
|
-
- Description below: text-sm text-muted-foreground
|
|
4323
4341
|
|
|
4324
4342
|
### Footer
|
|
4325
4343
|
- Minimal: border-t border-border/10, py-10
|
|
@@ -4327,13 +4345,66 @@ var DESIGN_QUALITY = `
|
|
|
4327
4345
|
- Links: hover:text-foreground transition-colors
|
|
4328
4346
|
- Layout: flex justify-between on desktop, stack on mobile
|
|
4329
4347
|
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4348
|
+
NEVER include app-style elements (sidebar widgets, data tables, filters) on marketing pages.
|
|
4349
|
+
`;
|
|
4350
|
+
var DESIGN_QUALITY_APP = `
|
|
4351
|
+
## DESIGN QUALITY \u2014 APP PAGES
|
|
4352
|
+
|
|
4353
|
+
### Spacing
|
|
4354
|
+
- gap-4 md:gap-6 between sections
|
|
4355
|
+
- p-4 lg:p-6 content padding
|
|
4356
|
+
- Within cards: p-4 to p-6 (compact)
|
|
4357
|
+
- Between cards in grid: gap-4 (tight)
|
|
4358
|
+
|
|
4359
|
+
### Layout
|
|
4360
|
+
- Data tables, card grids, filters, stat rows
|
|
4361
|
+
- Page wrapper: flex flex-1 flex-col gap-4 p-4 lg:p-6
|
|
4362
|
+
- Stats grid: grid gap-4 md:grid-cols-2 lg:grid-cols-4
|
|
4363
|
+
- Content: functional, scannable, data-dense
|
|
4364
|
+
|
|
4365
|
+
NEVER include marketing sections (hero, pricing, testimonials) on app pages.
|
|
4336
4366
|
`;
|
|
4367
|
+
var DESIGN_QUALITY_AUTH = `
|
|
4368
|
+
## DESIGN QUALITY \u2014 AUTH PAGES
|
|
4369
|
+
|
|
4370
|
+
### Layout
|
|
4371
|
+
- Centered card: flex min-h-svh items-center justify-center p-6 md:p-10
|
|
4372
|
+
- Card width: w-full max-w-sm
|
|
4373
|
+
- No navigation, no section containers, no sidebar
|
|
4374
|
+
- Single focused form with clear CTA
|
|
4375
|
+
- Card \u2192 CardHeader (title + description) \u2192 CardContent (form) \u2192 CardFooter (link to other auth page)
|
|
4376
|
+
|
|
4377
|
+
NEVER include navigation bars, sidebars, or multi-section layouts on auth pages.
|
|
4378
|
+
`;
|
|
4379
|
+
function getDesignQualityForType(type) {
|
|
4380
|
+
switch (type) {
|
|
4381
|
+
case "marketing":
|
|
4382
|
+
return DESIGN_QUALITY_MARKETING;
|
|
4383
|
+
case "app":
|
|
4384
|
+
return DESIGN_QUALITY_APP;
|
|
4385
|
+
case "auth":
|
|
4386
|
+
return DESIGN_QUALITY_AUTH;
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
function inferPageTypeFromRoute(route) {
|
|
4390
|
+
const slug = route.replace(/^\//, "").split("/")[0] || "";
|
|
4391
|
+
const authSlugs = /* @__PURE__ */ new Set([
|
|
4392
|
+
"login",
|
|
4393
|
+
"register",
|
|
4394
|
+
"sign-up",
|
|
4395
|
+
"signup",
|
|
4396
|
+
"sign-in",
|
|
4397
|
+
"signin",
|
|
4398
|
+
"forgot-password",
|
|
4399
|
+
"reset-password"
|
|
4400
|
+
]);
|
|
4401
|
+
const marketingSlugs = /* @__PURE__ */ new Set(["pricing", "features", "about", "blog", "contact", "terms", "privacy"]);
|
|
4402
|
+
if (authSlugs.has(slug)) return "auth";
|
|
4403
|
+
if (marketingSlugs.has(slug) || slug === "") return "marketing";
|
|
4404
|
+
return "app";
|
|
4405
|
+
}
|
|
4406
|
+
var DESIGN_QUALITY = `${DESIGN_QUALITY_COMMON}
|
|
4407
|
+
${DESIGN_QUALITY_MARKETING}`;
|
|
4337
4408
|
var VISUAL_DEPTH = `
|
|
4338
4409
|
## VISUAL DEPTH TECHNIQUES (pick 1-3 per page based on context)
|
|
4339
4410
|
|
|
@@ -5335,11 +5406,14 @@ Return valid JSON only, no markdown code fence. Use this shape:
|
|
|
5335
5406
|
{ "requests": [ ... array of ModificationRequest ... ], "uxRecommendations": "optional markdown or omit key" }
|
|
5336
5407
|
Legacy: returning only a JSON array of requests is still accepted.`;
|
|
5337
5408
|
}
|
|
5338
|
-
function buildLightweightPagePrompt(pageName, route, styleContext, sharedComponentsSummary) {
|
|
5409
|
+
function buildLightweightPagePrompt(pageName, route, styleContext, sharedComponentsSummary, pageType) {
|
|
5410
|
+
const designConstraints = pageType ? getDesignQualityForType(pageType) : "";
|
|
5339
5411
|
return [
|
|
5340
5412
|
`Generate complete pageCode for a page called "${pageName}" at route "${route}".`,
|
|
5341
5413
|
`Output valid TSX with a default export React component.`,
|
|
5342
5414
|
`Use shadcn/ui components (import from @/components/ui/*). Use Tailwind CSS semantic tokens only.`,
|
|
5415
|
+
pageType ? `PAGE TYPE: ${pageType}` : "",
|
|
5416
|
+
designConstraints,
|
|
5343
5417
|
styleContext ? `Follow this style context:
|
|
5344
5418
|
${styleContext}` : "",
|
|
5345
5419
|
sharedComponentsSummary ? `Available shared components:
|
|
@@ -6449,11 +6523,36 @@ async function autoFixCode(code) {
|
|
|
6449
6523
|
fixes.push("fixed escaped closing quotes in strings");
|
|
6450
6524
|
}
|
|
6451
6525
|
const beforeEntityFix = fixed;
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6526
|
+
const isInsideAttrValue = (line, idx) => {
|
|
6527
|
+
let inQuote = false;
|
|
6528
|
+
let inAttr = false;
|
|
6529
|
+
for (let i = 0; i < idx; i++) {
|
|
6530
|
+
if (line[i] === "=" && line[i + 1] === '"') {
|
|
6531
|
+
inAttr = true;
|
|
6532
|
+
inQuote = true;
|
|
6533
|
+
i++;
|
|
6534
|
+
} else if (inAttr && line[i] === '"') {
|
|
6535
|
+
inAttr = false;
|
|
6536
|
+
inQuote = false;
|
|
6537
|
+
}
|
|
6538
|
+
}
|
|
6539
|
+
return inQuote;
|
|
6540
|
+
};
|
|
6541
|
+
fixed = fixed.split("\n").map((line) => {
|
|
6542
|
+
let l = line;
|
|
6543
|
+
l = l.replace(/<=/g, (m, offset) => isInsideAttrValue(line, offset) ? m : "<=");
|
|
6544
|
+
l = l.replace(/>=/g, (m, offset) => isInsideAttrValue(line, offset) ? m : ">=");
|
|
6545
|
+
l = l.replace(/&&/g, (m, offset) => isInsideAttrValue(line, offset) ? m : "&&");
|
|
6546
|
+
l = l.replace(
|
|
6547
|
+
/([\w)\]])\s*<\s*([\w(])/g,
|
|
6548
|
+
(m, p1, p2, offset) => isInsideAttrValue(line, offset) ? m : `${p1} < ${p2}`
|
|
6549
|
+
);
|
|
6550
|
+
l = l.replace(
|
|
6551
|
+
/([\w)\]])\s*>\s*([\w(])/g,
|
|
6552
|
+
(m, p1, p2, offset) => isInsideAttrValue(line, offset) ? m : `${p1} > ${p2}`
|
|
6553
|
+
);
|
|
6554
|
+
return l;
|
|
6555
|
+
}).join("\n");
|
|
6457
6556
|
if (fixed !== beforeEntityFix) {
|
|
6458
6557
|
fixes.push("Fixed syntax issues");
|
|
6459
6558
|
}
|
|
@@ -6928,7 +7027,16 @@ import { DesignSystemManager as DesignSystemManager3, loadManifest as loadManife
|
|
|
6928
7027
|
import chalk8 from "chalk";
|
|
6929
7028
|
var MARKETING_ROUTES = /* @__PURE__ */ new Set(["", "landing", "pricing", "about", "contact", "blog", "features"]);
|
|
6930
7029
|
var MIN_ANCHOR_PAGE_CODE_CHARS = 120;
|
|
6931
|
-
var AUTH_ROUTE_SLUGS = /* @__PURE__ */ new Set([
|
|
7030
|
+
var AUTH_ROUTE_SLUGS = /* @__PURE__ */ new Set([
|
|
7031
|
+
"login",
|
|
7032
|
+
"signin",
|
|
7033
|
+
"sign-in",
|
|
7034
|
+
"register",
|
|
7035
|
+
"sign-up",
|
|
7036
|
+
"signup",
|
|
7037
|
+
"forgot-password",
|
|
7038
|
+
"reset-password"
|
|
7039
|
+
]);
|
|
6932
7040
|
function inferRouteUsesAuthSegment(route) {
|
|
6933
7041
|
const slug = route.replace(/^\//, "").split("/")[0] || "";
|
|
6934
7042
|
return AUTH_ROUTE_SLUGS.has(slug);
|
|
@@ -6950,30 +7058,30 @@ function isMarketingRoute(route) {
|
|
|
6950
7058
|
const slug = route.replace(/^\//, "").split("/")[0] || "";
|
|
6951
7059
|
return MARKETING_ROUTES.has(slug);
|
|
6952
7060
|
}
|
|
6953
|
-
function routeToFsPath(projectRoot, route,
|
|
7061
|
+
function routeToFsPath(projectRoot, route, isAuthOrPlan) {
|
|
7062
|
+
const plan = typeof isAuthOrPlan === "object" ? isAuthOrPlan : void 0;
|
|
7063
|
+
const isAuth = typeof isAuthOrPlan === "boolean" ? isAuthOrPlan : false;
|
|
6954
7064
|
const slug = route.replace(/^\//, "");
|
|
6955
|
-
if (
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
return resolve5(projectRoot, "app", "page.tsx");
|
|
6960
|
-
}
|
|
6961
|
-
if (isMarketingRoute(route)) {
|
|
6962
|
-
return resolve5(projectRoot, "app", slug, "page.tsx");
|
|
7065
|
+
if (!slug) return resolve5(projectRoot, "app", "page.tsx");
|
|
7066
|
+
if (plan) {
|
|
7067
|
+
const group = getPageGroup(route, plan);
|
|
7068
|
+
if (group) return resolve5(projectRoot, "app", `(${group.id})`, slug, "page.tsx");
|
|
6963
7069
|
}
|
|
7070
|
+
if (isAuth) return resolve5(projectRoot, "app", "(auth)", slug || "login", "page.tsx");
|
|
7071
|
+
if (isMarketingRoute(route)) return resolve5(projectRoot, "app", slug, "page.tsx");
|
|
6964
7072
|
return resolve5(projectRoot, "app", "(app)", slug, "page.tsx");
|
|
6965
7073
|
}
|
|
6966
|
-
function routeToRelPath(route,
|
|
7074
|
+
function routeToRelPath(route, isAuthOrPlan) {
|
|
7075
|
+
const plan = typeof isAuthOrPlan === "object" ? isAuthOrPlan : void 0;
|
|
7076
|
+
const isAuth = typeof isAuthOrPlan === "boolean" ? isAuthOrPlan : false;
|
|
6967
7077
|
const slug = route.replace(/^\//, "");
|
|
6968
|
-
if (
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
return "app/page.tsx";
|
|
6973
|
-
}
|
|
6974
|
-
if (isMarketingRoute(route)) {
|
|
6975
|
-
return `app/${slug}/page.tsx`;
|
|
7078
|
+
if (!slug) return "app/page.tsx";
|
|
7079
|
+
if (plan) {
|
|
7080
|
+
const group = getPageGroup(route, plan);
|
|
7081
|
+
if (group) return `app/(${group.id})/${slug}/page.tsx`;
|
|
6976
7082
|
}
|
|
7083
|
+
if (isAuth) return `app/(auth)/${slug || "login"}/page.tsx`;
|
|
7084
|
+
if (isMarketingRoute(route)) return `app/${slug}/page.tsx`;
|
|
6977
7085
|
return `app/(app)/${slug}/page.tsx`;
|
|
6978
7086
|
}
|
|
6979
7087
|
function deduplicatePages(pages) {
|
|
@@ -6998,13 +7106,24 @@ function extractComponentIdsFromCode(code) {
|
|
|
6998
7106
|
}
|
|
6999
7107
|
return ids;
|
|
7000
7108
|
}
|
|
7001
|
-
async function warnInlineDuplicates(projectRoot, pageName, pageCode, manifest) {
|
|
7109
|
+
async function warnInlineDuplicates(projectRoot, pageName, route, pageCode, manifest, plan) {
|
|
7002
7110
|
const sectionOrWidget = manifest.shared.filter((e) => e.type === "section" || e.type === "widget");
|
|
7003
7111
|
if (sectionOrWidget.length === 0) return;
|
|
7112
|
+
const plannedForPage = plan ? new Set(plan.sharedComponents.filter((c) => c.usedBy.includes(route)).map((c) => c.name)) : null;
|
|
7004
7113
|
for (const e of sectionOrWidget) {
|
|
7114
|
+
if (plannedForPage && !plannedForPage.has(e.name)) continue;
|
|
7005
7115
|
const kebab = e.file.replace(/^components\/shared\//, "").replace(/\.tsx$/, "");
|
|
7006
7116
|
const hasImport = pageCode.includes(`@/components/shared/${kebab}`);
|
|
7007
7117
|
if (hasImport) continue;
|
|
7118
|
+
if (plannedForPage) {
|
|
7119
|
+
console.log(
|
|
7120
|
+
chalk8.yellow(
|
|
7121
|
+
`
|
|
7122
|
+
\u26A0 Page "${pageName}" should use shared component ${e.name} (per architecture plan) but it's not imported. Import from @/components/shared/${kebab}`
|
|
7123
|
+
)
|
|
7124
|
+
);
|
|
7125
|
+
continue;
|
|
7126
|
+
}
|
|
7008
7127
|
const sameNameAsTag = new RegExp(`<\\/?${e.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s>]`).test(pageCode);
|
|
7009
7128
|
if (sameNameAsTag) {
|
|
7010
7129
|
console.log(
|
|
@@ -7616,6 +7735,7 @@ async function pMap(items, fn, concurrency = 3) {
|
|
|
7616
7735
|
}
|
|
7617
7736
|
|
|
7618
7737
|
// src/commands/chat/split-generator.ts
|
|
7738
|
+
import chalk9 from "chalk";
|
|
7619
7739
|
function buildExistingPagesContext(config2) {
|
|
7620
7740
|
const pages = config2.pages || [];
|
|
7621
7741
|
const analyzed = pages.filter((p) => p.pageAnalysis);
|
|
@@ -7712,6 +7832,21 @@ Before implementing any section, check this list. Import and use matching compon
|
|
|
7712
7832
|
|
|
7713
7833
|
${sharedComponentsSummary}`;
|
|
7714
7834
|
}
|
|
7835
|
+
function formatPlanSummary(plan) {
|
|
7836
|
+
if (plan.groups.length === 0) return "";
|
|
7837
|
+
const groupLines = plan.groups.map((g) => ` Group "${g.id}" (layout: ${g.layout}): ${g.pages.join(", ")}`);
|
|
7838
|
+
const compLines = plan.sharedComponents.map(
|
|
7839
|
+
(c) => ` ${c.name} (${c.type}) \u2014 ${c.description}; usedBy: ${c.usedBy.join(", ")}`
|
|
7840
|
+
);
|
|
7841
|
+
const parts = [`ARCHITECTURE PLAN:
|
|
7842
|
+
Groups:
|
|
7843
|
+
${groupLines.join("\n")}`];
|
|
7844
|
+
if (compLines.length > 0) {
|
|
7845
|
+
parts.push(`Shared Components:
|
|
7846
|
+
${compLines.join("\n")}`);
|
|
7847
|
+
}
|
|
7848
|
+
return parts.join("\n");
|
|
7849
|
+
}
|
|
7715
7850
|
async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts) {
|
|
7716
7851
|
let pageNames = [];
|
|
7717
7852
|
spinner.start("Phase 1/5 \u2014 Planning pages...");
|
|
@@ -7745,7 +7880,7 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
|
|
|
7745
7880
|
}
|
|
7746
7881
|
if (pageNames.length === 0) {
|
|
7747
7882
|
spinner.fail("Could not determine pages to create");
|
|
7748
|
-
return [];
|
|
7883
|
+
return { requests: [], plan: null };
|
|
7749
7884
|
}
|
|
7750
7885
|
pageNames = deduplicatePages(pageNames);
|
|
7751
7886
|
const hasHomePage = pageNames.some((p) => p.route === "/");
|
|
@@ -7767,7 +7902,41 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
|
|
|
7767
7902
|
const allRoutes = pageNames.map((p) => p.route).join(", ");
|
|
7768
7903
|
const allPagesList = pageNames.map((p) => `${p.name} (${p.route})`).join(", ");
|
|
7769
7904
|
const inferredNote = inferred.length > 0 ? ` (${inferred.length} auto-inferred)` : "";
|
|
7770
|
-
spinner.succeed(`Phase 1/
|
|
7905
|
+
spinner.succeed(`Phase 1/6 \u2014 Found ${pageNames.length} pages${inferredNote}: ${allPagesList}`);
|
|
7906
|
+
let plan = null;
|
|
7907
|
+
if (parseOpts.projectRoot) {
|
|
7908
|
+
spinner.start("Phase 2/6 \u2014 Generating architecture plan...");
|
|
7909
|
+
try {
|
|
7910
|
+
const ai = await createAIProvider(provider ?? "auto");
|
|
7911
|
+
const layoutHint = modCtx.config.navigation?.type || null;
|
|
7912
|
+
plan = await generateArchitecturePlan(pageNames, message, ai, layoutHint);
|
|
7913
|
+
if (plan) {
|
|
7914
|
+
const groupsSummary = plan.groups.map((g) => `${g.id} (${g.layout}, ${g.pages.length} pages)`).join(", ");
|
|
7915
|
+
const sharedSummary = plan.sharedComponents.length > 0 ? plan.sharedComponents.map((c) => `${c.name} \u2192 ${c.usedBy.join(", ")}`).join(" | ") : "";
|
|
7916
|
+
const totalPages = plan.groups.reduce((sum, g) => sum + g.pages.length, 0);
|
|
7917
|
+
spinner.succeed(`Phase 2/6 \u2014 Architecture plan created`);
|
|
7918
|
+
console.log(chalk9.dim(` Groups: ${groupsSummary}`));
|
|
7919
|
+
if (sharedSummary) console.log(chalk9.dim(` Shared: ${sharedSummary}`));
|
|
7920
|
+
console.log(chalk9.dim(` Total: ${totalPages} pages, ${plan.sharedComponents.length} shared components`));
|
|
7921
|
+
if (plan.sharedComponents.length > 0 && parseOpts.projectRoot) {
|
|
7922
|
+
const allDeps = new Set(plan.sharedComponents.flatMap((c) => c.shadcnDeps));
|
|
7923
|
+
if (allDeps.size > 0) {
|
|
7924
|
+
const componentProvider = getComponentProvider();
|
|
7925
|
+
for (const dep of allDeps) {
|
|
7926
|
+
try {
|
|
7927
|
+
await componentProvider.installComponent(dep, parseOpts.projectRoot);
|
|
7928
|
+
} catch {
|
|
7929
|
+
}
|
|
7930
|
+
}
|
|
7931
|
+
}
|
|
7932
|
+
}
|
|
7933
|
+
} else {
|
|
7934
|
+
spinner.warn("Phase 2/6 \u2014 Plan generation failed (continuing without plan)");
|
|
7935
|
+
}
|
|
7936
|
+
} catch {
|
|
7937
|
+
spinner.warn("Phase 2/6 \u2014 Plan generation failed (continuing without plan)");
|
|
7938
|
+
}
|
|
7939
|
+
}
|
|
7771
7940
|
const homeIdx = pageNames.findIndex((p) => p.route === "/");
|
|
7772
7941
|
const homePage = homeIdx !== -1 ? pageNames[homeIdx] : pageNames[0];
|
|
7773
7942
|
const remainingPages = pageNames.filter((_, i) => i !== (homeIdx !== -1 ? homeIdx : 0));
|
|
@@ -7781,12 +7950,12 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
|
|
|
7781
7950
|
if (existingCode) {
|
|
7782
7951
|
reusedExistingAnchor = true;
|
|
7783
7952
|
homePageCode = existingCode;
|
|
7784
|
-
spinner.start(`Phase
|
|
7785
|
-
spinner.succeed(`Phase
|
|
7953
|
+
spinner.start(`Phase 3/6 \u2014 Loading ${homePage.name} from disk (style anchor)...`);
|
|
7954
|
+
spinner.succeed(`Phase 3/6 \u2014 Reused existing ${homePage.name} page (skipped AI regeneration)`);
|
|
7786
7955
|
}
|
|
7787
7956
|
}
|
|
7788
7957
|
if (!reusedExistingAnchor) {
|
|
7789
|
-
spinner.start(`Phase
|
|
7958
|
+
spinner.start(`Phase 3/6 \u2014 Generating ${homePage.name} page (sets design direction)...`);
|
|
7790
7959
|
try {
|
|
7791
7960
|
const homeResult = await parseModification(
|
|
7792
7961
|
`Create ONE page called "${homePage.name}" at route "${homePage.route}". Context: ${message}. This REPLACES the default placeholder page \u2014 generate a complete, content-rich landing page for the project described above. Generate complete pageCode. Include a branded site-wide <header> with navigation links to ALL these pages: ${allPagesList}. Use these EXACT routes in navigation: ${allRoutes}. Include a <footer> at the bottom. Make it visually polished \u2014 this page sets the design direction for the entire site. Do not generate other pages.`,
|
|
@@ -7808,73 +7977,102 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
|
|
|
7808
7977
|
changes: { id: homePage.id, name: homePage.name, route: homePage.route }
|
|
7809
7978
|
};
|
|
7810
7979
|
}
|
|
7811
|
-
spinner.succeed(`Phase
|
|
7980
|
+
spinner.succeed(`Phase 3/6 \u2014 ${homePage.name} page generated`);
|
|
7812
7981
|
}
|
|
7813
|
-
spinner.start("Phase
|
|
7982
|
+
spinner.start("Phase 4/6 \u2014 Extracting design patterns...");
|
|
7814
7983
|
const styleContext = homePageCode ? extractStyleContext(homePageCode) : "";
|
|
7815
7984
|
if (styleContext) {
|
|
7816
7985
|
const lineCount = styleContext.split("\n").length - 1;
|
|
7817
7986
|
const source = reusedExistingAnchor ? `${homePage.name} (existing file)` : homePage.name;
|
|
7818
|
-
spinner.succeed(`Phase
|
|
7987
|
+
spinner.succeed(`Phase 4/6 \u2014 Extracted ${lineCount} style patterns from ${source}`);
|
|
7819
7988
|
} else {
|
|
7820
|
-
spinner.succeed("Phase
|
|
7989
|
+
spinner.succeed("Phase 4/6 \u2014 No style patterns extracted (anchor page had no code)");
|
|
7821
7990
|
}
|
|
7822
|
-
if (remainingPages.length >= 2 &&
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
if (!shouldSkip) {
|
|
7826
|
-
spinner.start("Phase 3.5/5 \u2014 Extracting shared components...");
|
|
7991
|
+
if (remainingPages.length >= 2 && projectRoot) {
|
|
7992
|
+
if (plan && plan.sharedComponents.length > 0) {
|
|
7993
|
+
spinner.start(`Phase 4.5/6 \u2014 Generating ${plan.sharedComponents.length} shared components from plan...`);
|
|
7827
7994
|
try {
|
|
7828
|
-
const
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7995
|
+
const { generateSharedComponentsFromPlan } = await import("./plan-generator-XKMZTEGK.js");
|
|
7996
|
+
const generated = await generateSharedComponentsFromPlan(
|
|
7997
|
+
plan,
|
|
7998
|
+
styleContext,
|
|
7999
|
+
projectRoot,
|
|
8000
|
+
await createAIProvider(provider ?? "auto")
|
|
8001
|
+
);
|
|
8002
|
+
if (generated.length > 0) {
|
|
8003
|
+
const updatedManifest = await loadManifest5(projectRoot);
|
|
8004
|
+
parseOpts.sharedComponentsSummary = buildSharedComponentsSummary(updatedManifest);
|
|
8005
|
+
const names = generated.map((c) => c.name).join(", ");
|
|
8006
|
+
spinner.succeed(`Phase 4.5/6 \u2014 Generated ${generated.length} shared components (${names})`);
|
|
7833
8007
|
} else {
|
|
7834
|
-
spinner.succeed("Phase
|
|
8008
|
+
spinner.succeed("Phase 4.5/6 \u2014 No shared components generated");
|
|
7835
8009
|
}
|
|
7836
8010
|
} catch {
|
|
7837
|
-
spinner.warn("Phase
|
|
8011
|
+
spinner.warn("Phase 4.5/6 \u2014 Could not generate shared components (continuing without)");
|
|
8012
|
+
}
|
|
8013
|
+
} else if (homePageCode) {
|
|
8014
|
+
const manifest = await loadManifest5(projectRoot);
|
|
8015
|
+
const shouldSkip = reusedExistingAnchor && manifest.shared.some((e) => e.type !== "layout");
|
|
8016
|
+
if (!shouldSkip) {
|
|
8017
|
+
spinner.start("Phase 4.5/6 \u2014 Extracting shared components (legacy)...");
|
|
8018
|
+
try {
|
|
8019
|
+
const extraction = await extractSharedComponents(homePageCode, projectRoot, provider ?? "auto");
|
|
8020
|
+
parseOpts.sharedComponentsSummary = extraction.summary;
|
|
8021
|
+
if (extraction.components.length > 0) {
|
|
8022
|
+
const names = extraction.components.map((c) => c.name).join(", ");
|
|
8023
|
+
spinner.succeed(`Phase 4.5/6 \u2014 Extracted ${extraction.components.length} shared components (${names})`);
|
|
8024
|
+
} else {
|
|
8025
|
+
spinner.succeed("Phase 4.5/6 \u2014 No shared components extracted");
|
|
8026
|
+
}
|
|
8027
|
+
} catch {
|
|
8028
|
+
spinner.warn("Phase 4.5/6 \u2014 Could not extract shared components (continuing without)");
|
|
8029
|
+
}
|
|
7838
8030
|
}
|
|
7839
8031
|
}
|
|
7840
8032
|
}
|
|
7841
8033
|
if (remainingPages.length === 0) {
|
|
7842
|
-
return homeRequest ? [homeRequest] : [];
|
|
8034
|
+
return { requests: homeRequest ? [homeRequest] : [], plan };
|
|
7843
8035
|
}
|
|
7844
|
-
spinner.start(`Phase
|
|
8036
|
+
spinner.start(`Phase 5/6 \u2014 Generating ${remainingPages.length} pages in parallel...`);
|
|
7845
8037
|
const sharedLayoutNote = "Header and Footer are shared components rendered by the root layout. Do NOT include any site-wide <header>, <nav>, or <footer> in this page. Start with the main content directly.";
|
|
7846
8038
|
const sharedComponentsNote = buildSharedComponentsNote(parseOpts.sharedComponentsSummary);
|
|
7847
8039
|
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="#".`;
|
|
7848
8040
|
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.';
|
|
8041
|
+
const planSummaryNote = plan ? formatPlanSummary(plan) : "";
|
|
7849
8042
|
const existingPagesContext = buildExistingPagesContext(modCtx.config);
|
|
7850
8043
|
const AI_CONCURRENCY = 3;
|
|
7851
|
-
let
|
|
8044
|
+
let phase5Done = 0;
|
|
7852
8045
|
const remainingRequests = await pMap(
|
|
7853
8046
|
remainingPages,
|
|
7854
8047
|
async ({ name, id, route }) => {
|
|
7855
8048
|
const isAuth = isAuthRoute(route) || isAuthRoute(name);
|
|
8049
|
+
const pageType = plan ? getPageType(route, plan) : inferPageTypeFromRoute(route);
|
|
8050
|
+
const designConstraints = getDesignQualityForType(pageType);
|
|
7856
8051
|
const authNote = isAuth ? 'For this auth page: use centered card layout with outer div className="flex min-h-svh flex-col items-center justify-center p-6 md:p-10" and inner div className="w-full max-w-sm". Do NOT use section containers or full-width wrappers. The auth layout provides centering \u2014 just output the card content.' : void 0;
|
|
7857
8052
|
const prompt = [
|
|
7858
8053
|
`Create ONE page called "${name}" at route "${route}".`,
|
|
7859
8054
|
`Context: ${message}.`,
|
|
7860
8055
|
`Generate complete pageCode for this single page only. Do not generate other pages.`,
|
|
8056
|
+
`PAGE TYPE: ${pageType}`,
|
|
8057
|
+
designConstraints,
|
|
7861
8058
|
sharedLayoutNote,
|
|
7862
8059
|
sharedComponentsNote,
|
|
7863
8060
|
routeNote,
|
|
7864
8061
|
alignmentNote,
|
|
7865
8062
|
authNote,
|
|
8063
|
+
planSummaryNote,
|
|
7866
8064
|
existingPagesContext,
|
|
7867
8065
|
styleContext
|
|
7868
8066
|
].filter(Boolean).join("\n\n");
|
|
7869
8067
|
try {
|
|
7870
8068
|
const result = await parseModification(prompt, modCtx, provider, parseOpts);
|
|
7871
|
-
|
|
7872
|
-
spinner.text = `Phase
|
|
8069
|
+
phase5Done++;
|
|
8070
|
+
spinner.text = `Phase 5/6 \u2014 ${phase5Done}/${remainingPages.length} pages generated...`;
|
|
7873
8071
|
const codePage = result.requests.find((r) => r.type === "add-page");
|
|
7874
8072
|
return codePage || { type: "add-page", target: "new", changes: { id, name, route } };
|
|
7875
8073
|
} catch {
|
|
7876
|
-
|
|
7877
|
-
spinner.text = `Phase
|
|
8074
|
+
phase5Done++;
|
|
8075
|
+
spinner.text = `Phase 5/6 \u2014 ${phase5Done}/${remainingPages.length} pages generated...`;
|
|
7878
8076
|
return { type: "add-page", target: "new", changes: { id, name, route } };
|
|
7879
8077
|
}
|
|
7880
8078
|
},
|
|
@@ -7889,18 +8087,18 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
|
|
|
7889
8087
|
const pageName = page.name || page.id || "page";
|
|
7890
8088
|
const pageRoute = page.route || `/${pageName.toLowerCase()}`;
|
|
7891
8089
|
try {
|
|
8090
|
+
const retryPageType = plan ? getPageType(pageRoute, plan) : inferPageTypeFromRoute(pageRoute);
|
|
7892
8091
|
const lightweightPrompt = buildLightweightPagePrompt(
|
|
7893
8092
|
pageName,
|
|
7894
8093
|
pageRoute,
|
|
7895
8094
|
styleContext || "",
|
|
7896
|
-
parseOpts.sharedComponentsSummary
|
|
7897
|
-
|
|
7898
|
-
const retryResult = await parseModification(
|
|
7899
|
-
lightweightPrompt,
|
|
7900
|
-
modCtx,
|
|
7901
|
-
provider,
|
|
7902
|
-
{ ...parseOpts, lightweight: true }
|
|
8095
|
+
parseOpts.sharedComponentsSummary,
|
|
8096
|
+
retryPageType
|
|
7903
8097
|
);
|
|
8098
|
+
const retryResult = await parseModification(lightweightPrompt, modCtx, provider, {
|
|
8099
|
+
...parseOpts,
|
|
8100
|
+
lightweight: true
|
|
8101
|
+
});
|
|
7904
8102
|
const codePage = retryResult.requests.find((r) => r.type === "add-page");
|
|
7905
8103
|
if (codePage && codePage.changes?.pageCode) {
|
|
7906
8104
|
const idx = allRequests.indexOf(req);
|
|
@@ -7911,8 +8109,8 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
|
|
|
7911
8109
|
}
|
|
7912
8110
|
}
|
|
7913
8111
|
const withCode = allRequests.filter((r) => r.changes?.pageCode).length;
|
|
7914
|
-
spinner.succeed(`Phase
|
|
7915
|
-
return allRequests;
|
|
8112
|
+
spinner.succeed(`Phase 5/6 \u2014 Generated ${allRequests.length} pages (${withCode} with full code)`);
|
|
8113
|
+
return { requests: allRequests, plan };
|
|
7916
8114
|
}
|
|
7917
8115
|
var SharedExtractionItemSchema = z.object({
|
|
7918
8116
|
name: z.string().min(2).max(50),
|
|
@@ -8026,7 +8224,7 @@ function extractAppNameFromPrompt(prompt) {
|
|
|
8026
8224
|
import { resolve as resolve7 } from "path";
|
|
8027
8225
|
import { mkdir as mkdir4 } from "fs/promises";
|
|
8028
8226
|
import { dirname as dirname6 } from "path";
|
|
8029
|
-
import
|
|
8227
|
+
import chalk12 from "chalk";
|
|
8030
8228
|
import {
|
|
8031
8229
|
getTemplateForPageType,
|
|
8032
8230
|
loadManifest as loadManifest6,
|
|
@@ -8047,7 +8245,7 @@ import {
|
|
|
8047
8245
|
TailwindConfigGenerator
|
|
8048
8246
|
} from "@getcoherent/core";
|
|
8049
8247
|
import { integrateSharedLayoutIntoRootLayout as integrateSharedLayoutIntoRootLayout2, generateSharedComponent as generateSharedComponent3 } from "@getcoherent/core";
|
|
8050
|
-
import
|
|
8248
|
+
import chalk10 from "chalk";
|
|
8051
8249
|
|
|
8052
8250
|
// src/utils/file-hashes.ts
|
|
8053
8251
|
import { createHash } from "crypto";
|
|
@@ -8151,7 +8349,7 @@ async function canOverwriteShared(projectRoot, componentFile, storedHashes) {
|
|
|
8151
8349
|
if (!storedHash) return true;
|
|
8152
8350
|
const edited = await isManuallyEdited(filePath, storedHash);
|
|
8153
8351
|
if (edited) {
|
|
8154
|
-
console.log(
|
|
8352
|
+
console.log(chalk10.yellow(` \u26A0 Skipping ${componentFile} \u2014 manually edited since last generation`));
|
|
8155
8353
|
}
|
|
8156
8354
|
return !edited;
|
|
8157
8355
|
}
|
|
@@ -8214,7 +8412,7 @@ async function regenerateLayout(config2, projectRoot, options = { navChanged: fa
|
|
|
8214
8412
|
await ensureAppRouteGroupLayout(projectRoot, config2.navigation?.type, options.navChanged);
|
|
8215
8413
|
} catch (err) {
|
|
8216
8414
|
if (process.env.COHERENT_DEBUG === "1") {
|
|
8217
|
-
console.log(
|
|
8415
|
+
console.log(chalk10.dim("Layout integration warning:", err));
|
|
8218
8416
|
}
|
|
8219
8417
|
}
|
|
8220
8418
|
}
|
|
@@ -8282,6 +8480,59 @@ export default function AppLayout({
|
|
|
8282
8480
|
}
|
|
8283
8481
|
`;
|
|
8284
8482
|
}
|
|
8483
|
+
function buildGroupLayoutCode(layout, _pages) {
|
|
8484
|
+
if (layout === "sidebar" || layout === "both") {
|
|
8485
|
+
return `import { Sidebar } from '@/components/shared/sidebar'
|
|
8486
|
+
|
|
8487
|
+
export default function GroupLayout({
|
|
8488
|
+
children,
|
|
8489
|
+
}: {
|
|
8490
|
+
children: React.ReactNode
|
|
8491
|
+
}) {
|
|
8492
|
+
return (
|
|
8493
|
+
<div className="flex min-h-[calc(100vh-3.5rem)]">
|
|
8494
|
+
<Sidebar />
|
|
8495
|
+
<main className="flex-1 px-4 sm:px-6 lg:px-8 py-6">
|
|
8496
|
+
{children}
|
|
8497
|
+
</main>
|
|
8498
|
+
</div>
|
|
8499
|
+
)
|
|
8500
|
+
}
|
|
8501
|
+
`;
|
|
8502
|
+
}
|
|
8503
|
+
if (layout === "none") {
|
|
8504
|
+
return `export default function GroupLayout({
|
|
8505
|
+
children,
|
|
8506
|
+
}: {
|
|
8507
|
+
children: React.ReactNode
|
|
8508
|
+
}) {
|
|
8509
|
+
return <>{children}</>
|
|
8510
|
+
}
|
|
8511
|
+
`;
|
|
8512
|
+
}
|
|
8513
|
+
return `export default function GroupLayout({
|
|
8514
|
+
children,
|
|
8515
|
+
}: {
|
|
8516
|
+
children: React.ReactNode
|
|
8517
|
+
}) {
|
|
8518
|
+
return (
|
|
8519
|
+
<main className="mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8 py-6">
|
|
8520
|
+
{children}
|
|
8521
|
+
</main>
|
|
8522
|
+
)
|
|
8523
|
+
}
|
|
8524
|
+
`;
|
|
8525
|
+
}
|
|
8526
|
+
async function ensurePlanGroupLayouts(projectRoot, plan) {
|
|
8527
|
+
const { mkdir: mkdirAsync } = await import("fs/promises");
|
|
8528
|
+
for (const group of plan.groups) {
|
|
8529
|
+
const groupDir = resolve6(projectRoot, "app", `(${group.id})`);
|
|
8530
|
+
await mkdirAsync(groupDir, { recursive: true });
|
|
8531
|
+
const layoutPath = resolve6(groupDir, "layout.tsx");
|
|
8532
|
+
const code = buildGroupLayoutCode(group.layout, group.pages);
|
|
8533
|
+
await writeFile(layoutPath, code);
|
|
8534
|
+
}
|
|
8535
|
+
}
|
|
8285
8536
|
async function regenerateFiles(modified, config2, projectRoot, options = { navChanged: false }) {
|
|
8286
8537
|
const componentIds = /* @__PURE__ */ new Set();
|
|
8287
8538
|
const pageIds = /* @__PURE__ */ new Set();
|
|
@@ -8299,7 +8550,7 @@ async function regenerateFiles(modified, config2, projectRoot, options = { navCh
|
|
|
8299
8550
|
});
|
|
8300
8551
|
const sharedInstalled = await scanAndInstallSharedDeps(projectRoot);
|
|
8301
8552
|
if (sharedInstalled.length > 0 && process.env.COHERENT_DEBUG === "1") {
|
|
8302
|
-
console.log(
|
|
8553
|
+
console.log(chalk10.dim(` Auto-installed shared deps: ${sharedInstalled.join(", ")}`));
|
|
8303
8554
|
}
|
|
8304
8555
|
}
|
|
8305
8556
|
if (componentIds.size > 0) {
|
|
@@ -8356,7 +8607,7 @@ function extractBalancedTag(source, tagName) {
|
|
|
8356
8607
|
}
|
|
8357
8608
|
|
|
8358
8609
|
// src/commands/chat/reporting.ts
|
|
8359
|
-
import
|
|
8610
|
+
import chalk11 from "chalk";
|
|
8360
8611
|
function extractImportsFrom(code, fromPath) {
|
|
8361
8612
|
const escaped = fromPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8362
8613
|
const regex = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*['"\`]${escaped}[^'"\`]*['"\`]`, "g");
|
|
@@ -8378,27 +8629,27 @@ function printPostGenerationReport(opts) {
|
|
|
8378
8629
|
const iconCount = extractImportsFrom(code, "lucide-react").length;
|
|
8379
8630
|
const hasInstalled = postFixes.some((f) => f.startsWith("Installed:"));
|
|
8380
8631
|
const syntaxStatus = postFixes.length > 0 ? postFixes.some((f) => f.includes("metadata")) ? "fixed (escaped metadata quotes) \u2714" : "fixed \u2714" : "valid \u2714";
|
|
8381
|
-
console.log(
|
|
8632
|
+
console.log(chalk11.green(`
|
|
8382
8633
|
\u2705 Page "${pageTitle}" ${action} at ${filePath}
|
|
8383
8634
|
`));
|
|
8384
8635
|
if (uiComponents.length > 0) {
|
|
8385
|
-
console.log(
|
|
8636
|
+
console.log(chalk11.dim(` Components: ${uiComponents.join(", ")} (from @/components/ui)`));
|
|
8386
8637
|
}
|
|
8387
8638
|
if (inCodeShared.length > 0) {
|
|
8388
|
-
console.log(
|
|
8639
|
+
console.log(chalk11.dim(` Shared: ${inCodeShared.map((s) => `${s.id} (${s.name})`).join(", ")}`));
|
|
8389
8640
|
}
|
|
8390
8641
|
if (layoutShared.length > 0) {
|
|
8391
|
-
console.log(
|
|
8642
|
+
console.log(chalk11.dim(` Layout: ${layoutShared.map((l) => `${l.id} (${l.name})`).join(", ")} via layout.tsx`));
|
|
8392
8643
|
}
|
|
8393
8644
|
if (iconCount > 0) {
|
|
8394
|
-
console.log(
|
|
8645
|
+
console.log(chalk11.dim(` Icons: ${iconCount} from lucide-react`));
|
|
8395
8646
|
}
|
|
8396
8647
|
if (hasInstalled) {
|
|
8397
|
-
console.log(
|
|
8648
|
+
console.log(chalk11.dim(" Dependencies: installed \u2714"));
|
|
8398
8649
|
}
|
|
8399
|
-
console.log(
|
|
8650
|
+
console.log(chalk11.dim(` Syntax: ${syntaxStatus}`));
|
|
8400
8651
|
if (route) {
|
|
8401
|
-
console.log(
|
|
8652
|
+
console.log(chalk11.cyan(`
|
|
8402
8653
|
Preview: http://localhost:3000${route}`));
|
|
8403
8654
|
}
|
|
8404
8655
|
console.log("");
|
|
@@ -8406,35 +8657,35 @@ function printPostGenerationReport(opts) {
|
|
|
8406
8657
|
function printSharedComponentReport(opts) {
|
|
8407
8658
|
const { id, name, file, instruction, postFixes = [] } = opts;
|
|
8408
8659
|
const syntaxStatus = postFixes.length > 0 ? "fixed \u2714" : "valid \u2714";
|
|
8409
|
-
console.log(
|
|
8660
|
+
console.log(chalk11.green(`
|
|
8410
8661
|
\u2705 Updated ${id} (${name}) at ${file}
|
|
8411
8662
|
`));
|
|
8412
8663
|
if (instruction) {
|
|
8413
8664
|
const snippet = instruction.length > 60 ? instruction.slice(0, 57) + "..." : instruction;
|
|
8414
|
-
console.log(
|
|
8665
|
+
console.log(chalk11.dim(` Changed: ${snippet}`));
|
|
8415
8666
|
}
|
|
8416
|
-
console.log(
|
|
8417
|
-
console.log(
|
|
8667
|
+
console.log(chalk11.dim(" Affects: all pages via layout.tsx"));
|
|
8668
|
+
console.log(chalk11.dim(` Syntax: ${syntaxStatus}`));
|
|
8418
8669
|
console.log("");
|
|
8419
8670
|
}
|
|
8420
8671
|
function printLinkSharedReport(opts) {
|
|
8421
8672
|
const { sharedId, sharedName, pageTarget, route, postFixes = [] } = opts;
|
|
8422
8673
|
const syntaxStatus = postFixes.length > 0 ? "fixed \u2714" : "valid \u2714";
|
|
8423
|
-
console.log(
|
|
8674
|
+
console.log(chalk11.green(`
|
|
8424
8675
|
\u2705 Linked ${sharedId} (${sharedName}) to page "${pageTarget}"
|
|
8425
8676
|
`));
|
|
8426
|
-
console.log(
|
|
8427
|
-
console.log(
|
|
8677
|
+
console.log(chalk11.dim(` Syntax: ${syntaxStatus}`));
|
|
8678
|
+
console.log(chalk11.cyan(` Preview: http://localhost:3000${route}`));
|
|
8428
8679
|
console.log("");
|
|
8429
8680
|
}
|
|
8430
8681
|
function printPromoteAndLinkReport(opts) {
|
|
8431
8682
|
const { id, name, file, usedInFiles, postFixes = [] } = opts;
|
|
8432
8683
|
const syntaxStatus = postFixes.length > 0 ? "fixed \u2714" : "valid \u2714";
|
|
8433
|
-
console.log(
|
|
8684
|
+
console.log(chalk11.green(`
|
|
8434
8685
|
\u2705 Created ${id} (${name}) at ${file}
|
|
8435
8686
|
`));
|
|
8436
|
-
console.log(
|
|
8437
|
-
console.log(
|
|
8687
|
+
console.log(chalk11.dim(` Linked to: ${usedInFiles.length} page(s)`));
|
|
8688
|
+
console.log(chalk11.dim(` Syntax: ${syntaxStatus}`));
|
|
8438
8689
|
console.log("");
|
|
8439
8690
|
}
|
|
8440
8691
|
function showPreview(requests, results, config2, preflightInstalledNames) {
|
|
@@ -8452,23 +8703,23 @@ function showPreview(requests, results, config2, preflightInstalledNames) {
|
|
|
8452
8703
|
const modifiedSharedComponents = successfulPairs.filter(({ request }) => request.type === "modify-layout-block");
|
|
8453
8704
|
const modifiedPages = successfulPairs.filter(({ request }) => request.type === "update-page");
|
|
8454
8705
|
const tokenChanges = successfulPairs.filter(({ request }) => request.type === "update-token");
|
|
8455
|
-
console.log(
|
|
8706
|
+
console.log(chalk11.bold.cyan("\n\u{1F4CB} Changes Applied:\n"));
|
|
8456
8707
|
if (preflightInstalledNames && preflightInstalledNames.length > 0) {
|
|
8457
|
-
console.log(
|
|
8708
|
+
console.log(chalk11.cyan("\u{1F50D} Pre-flight check: Installed missing components:"));
|
|
8458
8709
|
preflightInstalledNames.forEach((name) => {
|
|
8459
|
-
console.log(
|
|
8710
|
+
console.log(chalk11.green(` \u2728 Auto-installed ${name}`));
|
|
8460
8711
|
});
|
|
8461
8712
|
console.log("");
|
|
8462
8713
|
}
|
|
8463
8714
|
if (addedComponents.length > 0) {
|
|
8464
8715
|
const names = addedComponents.map(({ request }) => request.changes.name).filter(Boolean);
|
|
8465
|
-
console.log(
|
|
8466
|
-
console.log(
|
|
8716
|
+
console.log(chalk11.green("\u{1F4E6} Components:"));
|
|
8717
|
+
console.log(chalk11.white(` \u2728 Auto-installed: ${names.join(", ")}`));
|
|
8467
8718
|
}
|
|
8468
8719
|
if (customComponents.length > 0) {
|
|
8469
8720
|
const names = customComponents.map(({ request }) => request.changes.name).filter(Boolean);
|
|
8470
|
-
if (addedComponents.length === 0) console.log(
|
|
8471
|
-
console.log(
|
|
8721
|
+
if (addedComponents.length === 0) console.log(chalk11.green("\u{1F4E6} Components:"));
|
|
8722
|
+
console.log(chalk11.white(` \u2728 Created: ${names.join(", ")}`));
|
|
8472
8723
|
}
|
|
8473
8724
|
const usedComponentIds = /* @__PURE__ */ new Set();
|
|
8474
8725
|
addedPages.forEach(({ request }) => {
|
|
@@ -8483,71 +8734,71 @@ function showPreview(requests, results, config2, preflightInstalledNames) {
|
|
|
8483
8734
|
]);
|
|
8484
8735
|
const reusedIds = [...usedComponentIds].filter((id) => !newComponentIds.has(id));
|
|
8485
8736
|
if (reusedIds.length > 0) {
|
|
8486
|
-
if (addedComponents.length === 0 && customComponents.length === 0) console.log(
|
|
8487
|
-
console.log(
|
|
8737
|
+
if (addedComponents.length === 0 && customComponents.length === 0) console.log(chalk11.green("\u{1F4E6} Components:"));
|
|
8738
|
+
console.log(chalk11.white(` \u{1F504} Reused: ${reusedIds.join(", ")}`));
|
|
8488
8739
|
}
|
|
8489
8740
|
if (addedComponents.length > 0 || customComponents.length > 0 || reusedIds.length > 0) {
|
|
8490
8741
|
console.log("");
|
|
8491
8742
|
}
|
|
8492
8743
|
if (addedPages.length > 0) {
|
|
8493
|
-
console.log(
|
|
8744
|
+
console.log(chalk11.green("\u{1F4C4} Pages Created:"));
|
|
8494
8745
|
addedPages.forEach(({ request }) => {
|
|
8495
8746
|
const page = request.changes;
|
|
8496
8747
|
const route = page.route || "/";
|
|
8497
|
-
console.log(
|
|
8498
|
-
console.log(
|
|
8499
|
-
console.log(
|
|
8748
|
+
console.log(chalk11.white(` \u2728 ${page.name || "Page"}`));
|
|
8749
|
+
console.log(chalk11.gray(` Route: ${route}`));
|
|
8750
|
+
console.log(chalk11.gray(` Sections: ${page.sections?.length ?? 0}`));
|
|
8500
8751
|
});
|
|
8501
8752
|
console.log("");
|
|
8502
8753
|
}
|
|
8503
8754
|
if (modifiedComponents.length > 0 || modifiedSharedComponents.length > 0 || modifiedPages.length > 0 || tokenChanges.length > 0) {
|
|
8504
|
-
console.log(
|
|
8755
|
+
console.log(chalk11.yellow("\u{1F527} Modified:"));
|
|
8505
8756
|
modifiedComponents.forEach(({ result }) => {
|
|
8506
|
-
console.log(
|
|
8757
|
+
console.log(chalk11.white(` \u2022 ${result.message}`));
|
|
8507
8758
|
});
|
|
8508
8759
|
modifiedSharedComponents.forEach(({ result }) => {
|
|
8509
|
-
console.log(
|
|
8760
|
+
console.log(chalk11.white(` \u2022 ${result.message}`));
|
|
8510
8761
|
});
|
|
8511
8762
|
modifiedPages.forEach(({ result }) => {
|
|
8512
|
-
console.log(
|
|
8763
|
+
console.log(chalk11.white(` \u2022 ${result.message}`));
|
|
8513
8764
|
});
|
|
8514
8765
|
tokenChanges.forEach(({ result }) => {
|
|
8515
|
-
console.log(
|
|
8766
|
+
console.log(chalk11.white(` \u2022 ${result.message}`));
|
|
8516
8767
|
});
|
|
8517
8768
|
console.log("");
|
|
8518
8769
|
}
|
|
8519
8770
|
if (failedPairs.length > 0) {
|
|
8520
|
-
console.log(
|
|
8771
|
+
console.log(chalk11.red("\u274C Failed modifications:"));
|
|
8521
8772
|
failedPairs.forEach(({ result }) => {
|
|
8522
|
-
console.log(
|
|
8773
|
+
console.log(chalk11.gray(` \u2716 ${result.message}`));
|
|
8523
8774
|
});
|
|
8524
8775
|
console.log("");
|
|
8525
8776
|
}
|
|
8526
8777
|
const successCount = successfulPairs.length;
|
|
8527
8778
|
const totalCount = results.length;
|
|
8528
8779
|
if (successCount === totalCount) {
|
|
8529
|
-
console.log(
|
|
8780
|
+
console.log(chalk11.green.bold(`\u2705 Success! ${successCount} modification(s) applied
|
|
8530
8781
|
`));
|
|
8531
8782
|
} else {
|
|
8532
|
-
console.log(
|
|
8783
|
+
console.log(chalk11.yellow.bold(`\u26A0\uFE0F Partial success: ${successCount}/${totalCount} modification(s) applied
|
|
8533
8784
|
`));
|
|
8534
8785
|
}
|
|
8535
8786
|
if (addedPages.length > 0) {
|
|
8536
8787
|
const firstPage = addedPages[0].request.changes;
|
|
8537
8788
|
const route = firstPage?.route || "/";
|
|
8538
|
-
console.log(
|
|
8539
|
-
console.log(
|
|
8540
|
-
console.log(
|
|
8541
|
-
console.log(
|
|
8789
|
+
console.log(chalk11.cyan("\u{1F680} What's next:\n"));
|
|
8790
|
+
console.log(chalk11.white(" \u{1F4FA} View in browser:"));
|
|
8791
|
+
console.log(chalk11.cyan(" coherent preview"));
|
|
8792
|
+
console.log(chalk11.gray(` \u2192 Opens http://localhost:3000${route}
|
|
8542
8793
|
`));
|
|
8543
|
-
console.log(
|
|
8544
|
-
console.log(
|
|
8545
|
-
console.log(
|
|
8794
|
+
console.log(chalk11.white(" \u{1F3A8} Customize:"));
|
|
8795
|
+
console.log(chalk11.cyan(' coherent chat "make buttons rounded"'));
|
|
8796
|
+
console.log(chalk11.cyan(` coherent chat "add hero section to ${firstPage?.name ?? "page"}"`));
|
|
8546
8797
|
console.log("");
|
|
8547
8798
|
} else if (successCount > 0) {
|
|
8548
|
-
console.log(
|
|
8549
|
-
console.log(
|
|
8550
|
-
console.log(
|
|
8799
|
+
console.log(chalk11.cyan("\u{1F680} What's next:\n"));
|
|
8800
|
+
console.log(chalk11.white(" \u{1F4FA} Preview changes:"));
|
|
8801
|
+
console.log(chalk11.cyan(" coherent preview\n"));
|
|
8551
8802
|
}
|
|
8552
8803
|
}
|
|
8553
8804
|
function getChangeDescription(request, config2) {
|
|
@@ -8696,8 +8947,8 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
8696
8947
|
const newCode = await ai.editSharedComponentCode(currentCode, instruction, resolved.name);
|
|
8697
8948
|
const { fixedCode, fixes } = await validateAndFixGeneratedCode(projectRoot, newCode, { isPage: false });
|
|
8698
8949
|
if (fixes.length > 0) {
|
|
8699
|
-
console.log(
|
|
8700
|
-
fixes.forEach((f) => console.log(
|
|
8950
|
+
console.log(chalk12.dim(" \u{1F527} Post-generation fixes:"));
|
|
8951
|
+
fixes.forEach((f) => console.log(chalk12.dim(` ${f}`)));
|
|
8701
8952
|
}
|
|
8702
8953
|
await writeFile(fullPath, fixedCode);
|
|
8703
8954
|
printSharedComponentReport({
|
|
@@ -8770,8 +9021,8 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
8770
9021
|
const newPageCode = await ai.replaceInlineWithShared(pageCode, sharedCode, resolved.name, changes?.blockHint);
|
|
8771
9022
|
const { fixedCode, fixes } = await validateAndFixGeneratedCode(projectRoot, newPageCode, { isPage: true });
|
|
8772
9023
|
if (fixes.length > 0) {
|
|
8773
|
-
console.log(
|
|
8774
|
-
fixes.forEach((f) => console.log(
|
|
9024
|
+
console.log(chalk12.dim(" \u{1F527} Post-generation fixes:"));
|
|
9025
|
+
fixes.forEach((f) => console.log(chalk12.dim(` ${f}`)));
|
|
8775
9026
|
}
|
|
8776
9027
|
await writeFile(pageFilePath, fixedCode);
|
|
8777
9028
|
const manifest = await loadManifest6(projectRoot);
|
|
@@ -8873,8 +9124,8 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
8873
9124
|
const newCode = await ai.replaceInlineWithShared(linkPageCode, sharedCode, created.name, blockHint);
|
|
8874
9125
|
const { fixedCode, fixes } = await validateAndFixGeneratedCode(projectRoot, newCode, { isPage: true });
|
|
8875
9126
|
if (fixes.length > 0) {
|
|
8876
|
-
console.log(
|
|
8877
|
-
fixes.forEach((f) => console.log(
|
|
9127
|
+
console.log(chalk12.dim(" \u{1F527} Post-generation fixes:"));
|
|
9128
|
+
fixes.forEach((f) => console.log(chalk12.dim(` ${f}`)));
|
|
8878
9129
|
}
|
|
8879
9130
|
await writeFile(fullPath, fixedCode);
|
|
8880
9131
|
usedInFiles.push(relPath);
|
|
@@ -8965,7 +9216,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
8965
9216
|
const aiPageCode = typeof page.pageCode === "string" && page.pageCode.trim() !== "" ? page.pageCode : void 0;
|
|
8966
9217
|
if (aiPageCode) {
|
|
8967
9218
|
finalPageCode = aiPageCode;
|
|
8968
|
-
if (DEBUG2) console.log(
|
|
9219
|
+
if (DEBUG2) console.log(chalk12.dim(` [pageCode] Using AI-generated pageCode (user content priority)`));
|
|
8969
9220
|
} else {
|
|
8970
9221
|
const inferredType = page.pageType || inferPageType(page.route || "", page.name || "");
|
|
8971
9222
|
if (inferredType) {
|
|
@@ -8980,19 +9231,19 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
8980
9231
|
const content = page.structuredContent || getDefaultContent(inferredType, page.name || pageName);
|
|
8981
9232
|
finalPageCode = templateFn(content, opts);
|
|
8982
9233
|
if (DEBUG2)
|
|
8983
|
-
console.log(
|
|
9234
|
+
console.log(chalk12.dim(` [template] Used "${inferredType}" template (inferred from route/name)`));
|
|
8984
9235
|
} catch {
|
|
8985
|
-
if (DEBUG2) console.log(
|
|
9236
|
+
if (DEBUG2) console.log(chalk12.dim(` [template] Failed for "${inferredType}"`));
|
|
8986
9237
|
}
|
|
8987
9238
|
}
|
|
8988
9239
|
}
|
|
8989
9240
|
}
|
|
8990
9241
|
if (!finalPageCode) {
|
|
8991
|
-
console.log(
|
|
9242
|
+
console.log(chalk12.yellow(`
|
|
8992
9243
|
\u26A0\uFE0F Page "${page.name || page.id}" has no generated code \u2014 it will appear empty.`));
|
|
8993
|
-
console.log(
|
|
9244
|
+
console.log(chalk12.dim(" This usually means the AI did not produce pageCode for this page."));
|
|
8994
9245
|
console.log(
|
|
8995
|
-
|
|
9246
|
+
chalk12.dim(
|
|
8996
9247
|
' Try running: coherent chat "regenerate the ' + (page.name || page.id) + ' page with full content"'
|
|
8997
9248
|
)
|
|
8998
9249
|
);
|
|
@@ -9046,7 +9297,9 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
9046
9297
|
}
|
|
9047
9298
|
const { code: layoutStripped, stripped } = stripInlineLayoutElements(codeToWrite);
|
|
9048
9299
|
codeToWrite = layoutStripped;
|
|
9049
|
-
|
|
9300
|
+
const currentPlan = projectRoot ? loadPlan(projectRoot) : null;
|
|
9301
|
+
const pageType = currentPlan ? getPageType(route, currentPlan) : isMarketingRoute(route) ? "marketing" : isAuth ? "auth" : "app";
|
|
9302
|
+
if (pageType === "app") {
|
|
9050
9303
|
const { code: normalized, fixed: wrapperFixed } = normalizePageWrapper(codeToWrite);
|
|
9051
9304
|
if (wrapperFixed) {
|
|
9052
9305
|
codeToWrite = normalized;
|
|
@@ -9056,8 +9309,8 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
9056
9309
|
const allFixes = [...postFixes, ...autoFixes];
|
|
9057
9310
|
if (stripped.length > 0) allFixes.push(`stripped inline ${stripped.join(", ")} (layout owns these)`);
|
|
9058
9311
|
if (allFixes.length > 0) {
|
|
9059
|
-
console.log(
|
|
9060
|
-
allFixes.forEach((f) => console.log(
|
|
9312
|
+
console.log(chalk12.dim(" \u{1F527} Post-generation fixes:"));
|
|
9313
|
+
allFixes.forEach((f) => console.log(chalk12.dim(` ${f}`)));
|
|
9061
9314
|
}
|
|
9062
9315
|
await writeFile(filePath, codeToWrite);
|
|
9063
9316
|
const pageIdx = dsm.getConfig().pages.findIndex((p) => p.id === page.id);
|
|
@@ -9069,7 +9322,15 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
9069
9322
|
pm.updateConfig(cfg);
|
|
9070
9323
|
}
|
|
9071
9324
|
const manifestForAudit = await loadManifest6(projectRoot);
|
|
9072
|
-
|
|
9325
|
+
const planForAudit = loadPlan(projectRoot);
|
|
9326
|
+
await warnInlineDuplicates(
|
|
9327
|
+
projectRoot,
|
|
9328
|
+
page.name || page.id || route.slice(1),
|
|
9329
|
+
route,
|
|
9330
|
+
codeToWrite,
|
|
9331
|
+
manifestForAudit,
|
|
9332
|
+
planForAudit ?? void 0
|
|
9333
|
+
);
|
|
9073
9334
|
const relFilePath = routeToRelPath(route, isAuth);
|
|
9074
9335
|
printPostGenerationReport({
|
|
9075
9336
|
action: "created",
|
|
@@ -9086,7 +9347,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
|
|
|
9086
9347
|
const errors = issues.filter((i) => i.severity === "error");
|
|
9087
9348
|
if (errors.length >= 2 && aiProvider) {
|
|
9088
9349
|
console.log(
|
|
9089
|
-
|
|
9350
|
+
chalk12.yellow(`
|
|
9090
9351
|
\u{1F504} ${errors.length} quality errors \u2014 attempting AI fix for ${page.name || page.id}...`)
|
|
9091
9352
|
);
|
|
9092
9353
|
try {
|
|
@@ -9115,7 +9376,7 @@ Rules:
|
|
|
9115
9376
|
await writeFile(filePath, codeToWrite);
|
|
9116
9377
|
issues = validatePageQuality(codeToWrite);
|
|
9117
9378
|
const finalErrors = issues.filter((i) => i.severity === "error").length;
|
|
9118
|
-
console.log(
|
|
9379
|
+
console.log(chalk12.green(` \u2714 Quality fix: ${errors.length} \u2192 ${finalErrors} errors`));
|
|
9119
9380
|
}
|
|
9120
9381
|
}
|
|
9121
9382
|
}
|
|
@@ -9124,15 +9385,15 @@ Rules:
|
|
|
9124
9385
|
}
|
|
9125
9386
|
const report = formatIssues(issues);
|
|
9126
9387
|
if (report) {
|
|
9127
|
-
console.log(
|
|
9388
|
+
console.log(chalk12.yellow(`
|
|
9128
9389
|
\u{1F50D} Quality check for ${page.name || page.id}:`));
|
|
9129
|
-
console.log(
|
|
9390
|
+
console.log(chalk12.dim(report));
|
|
9130
9391
|
}
|
|
9131
9392
|
const consistency = checkDesignConsistency(codeToWrite);
|
|
9132
9393
|
if (consistency.length > 0) {
|
|
9133
|
-
console.log(
|
|
9394
|
+
console.log(chalk12.yellow(`
|
|
9134
9395
|
\u{1F3A8} Design consistency for ${page.name || page.id}:`));
|
|
9135
|
-
consistency.forEach((w) => console.log(
|
|
9396
|
+
consistency.forEach((w) => console.log(chalk12.dim(` \u26A0 [${w.type}] ${w.message}`)));
|
|
9136
9397
|
}
|
|
9137
9398
|
}
|
|
9138
9399
|
}
|
|
@@ -9147,9 +9408,9 @@ Rules:
|
|
|
9147
9408
|
const changes = request.changes;
|
|
9148
9409
|
const instruction = originalMessage || (typeof changes?.instruction === "string" ? changes.instruction : void 0);
|
|
9149
9410
|
let resolvedPageCode = typeof changes?.pageCode === "string" && changes.pageCode.trim() !== "" ? changes.pageCode : void 0;
|
|
9150
|
-
if (DEBUG2 && instruction) console.log(
|
|
9411
|
+
if (DEBUG2 && instruction) console.log(chalk12.dim(` [update-page] instruction: ${instruction.slice(0, 120)}...`));
|
|
9151
9412
|
if (DEBUG2 && resolvedPageCode)
|
|
9152
|
-
console.log(
|
|
9413
|
+
console.log(chalk12.dim(` [update-page] has pageCode (${resolvedPageCode.length} chars)`));
|
|
9153
9414
|
const configChanges = { ...changes };
|
|
9154
9415
|
delete configChanges.pageCode;
|
|
9155
9416
|
delete configChanges.pageType;
|
|
@@ -9173,12 +9434,12 @@ Rules:
|
|
|
9173
9434
|
try {
|
|
9174
9435
|
currentCode = await readFile(absPath);
|
|
9175
9436
|
} catch {
|
|
9176
|
-
if (DEBUG2) console.log(
|
|
9437
|
+
if (DEBUG2) console.log(chalk12.dim(` [update-page] Could not read current file at ${absPath}`));
|
|
9177
9438
|
}
|
|
9178
9439
|
if (currentCode) {
|
|
9179
9440
|
const ai = await createAIProvider(aiProvider ?? "auto");
|
|
9180
9441
|
if (ai.editPageCode) {
|
|
9181
|
-
console.log(
|
|
9442
|
+
console.log(chalk12.dim(" \u270F\uFE0F Applying changes to existing page..."));
|
|
9182
9443
|
const coreRules = CORE_CONSTRAINTS;
|
|
9183
9444
|
const qualityRules = DESIGN_QUALITY;
|
|
9184
9445
|
const contextualRules = selectContextualRules(instruction);
|
|
@@ -9198,15 +9459,15 @@ ${contextualRules}
|
|
|
9198
9459
|
${routeRules}
|
|
9199
9460
|
${pagesCtx}`
|
|
9200
9461
|
);
|
|
9201
|
-
if (DEBUG2) console.log(
|
|
9462
|
+
if (DEBUG2) console.log(chalk12.dim(` [update-page] AI returned ${resolvedPageCode.length} chars`));
|
|
9202
9463
|
const editIssues = verifyIncrementalEdit(currentCode, resolvedPageCode);
|
|
9203
9464
|
if (editIssues.length > 0) {
|
|
9204
|
-
console.log(
|
|
9465
|
+
console.log(chalk12.yellow(`
|
|
9205
9466
|
\u26A0 Incremental edit issues for ${pageDef.name || pageDef.id}:`));
|
|
9206
|
-
editIssues.forEach((issue) => console.log(
|
|
9467
|
+
editIssues.forEach((issue) => console.log(chalk12.dim(` [${issue.type}] ${issue.message}`)));
|
|
9207
9468
|
}
|
|
9208
9469
|
} else {
|
|
9209
|
-
console.log(
|
|
9470
|
+
console.log(chalk12.yellow(" \u26A0 AI provider does not support editPageCode"));
|
|
9210
9471
|
}
|
|
9211
9472
|
}
|
|
9212
9473
|
}
|
|
@@ -9246,7 +9507,9 @@ ${pagesCtx}`
|
|
|
9246
9507
|
}
|
|
9247
9508
|
const { code: layoutStripped, stripped } = stripInlineLayoutElements(codeToWrite);
|
|
9248
9509
|
codeToWrite = layoutStripped;
|
|
9249
|
-
|
|
9510
|
+
const currentPlan2 = projectRoot ? loadPlan(projectRoot) : null;
|
|
9511
|
+
const pageType2 = currentPlan2 ? getPageType(route, currentPlan2) : isMarketingRoute(route) ? "marketing" : isAuth ? "auth" : "app";
|
|
9512
|
+
if (pageType2 === "app") {
|
|
9250
9513
|
const { code: normalized, fixed: wrapperFixed } = normalizePageWrapper(codeToWrite);
|
|
9251
9514
|
if (wrapperFixed) {
|
|
9252
9515
|
codeToWrite = normalized;
|
|
@@ -9256,8 +9519,8 @@ ${pagesCtx}`
|
|
|
9256
9519
|
const allFixes = [...postFixes, ...autoFixes];
|
|
9257
9520
|
if (stripped.length > 0) allFixes.push(`stripped inline ${stripped.join(", ")} (layout owns these)`);
|
|
9258
9521
|
if (allFixes.length > 0) {
|
|
9259
|
-
console.log(
|
|
9260
|
-
allFixes.forEach((f) => console.log(
|
|
9522
|
+
console.log(chalk12.dim(" \u{1F527} Post-generation fixes:"));
|
|
9523
|
+
allFixes.forEach((f) => console.log(chalk12.dim(` ${f}`)));
|
|
9261
9524
|
}
|
|
9262
9525
|
await writeFile(absPath, codeToWrite);
|
|
9263
9526
|
const updatePageIdx = dsm.getConfig().pages.findIndex((p) => p.id === pageDef.id);
|
|
@@ -9269,11 +9532,14 @@ ${pagesCtx}`
|
|
|
9269
9532
|
pm.updateConfig(cfg);
|
|
9270
9533
|
}
|
|
9271
9534
|
const manifestForAudit = await loadManifest6(projectRoot);
|
|
9535
|
+
const planForAudit2 = loadPlan(projectRoot);
|
|
9272
9536
|
await warnInlineDuplicates(
|
|
9273
9537
|
projectRoot,
|
|
9274
9538
|
pageDef.name || pageDef.id || route.slice(1),
|
|
9539
|
+
route,
|
|
9275
9540
|
codeToWrite,
|
|
9276
|
-
manifestForAudit
|
|
9541
|
+
manifestForAudit,
|
|
9542
|
+
planForAudit2 ?? void 0
|
|
9277
9543
|
);
|
|
9278
9544
|
const relFilePath = routeToRelPath(route, isAuth);
|
|
9279
9545
|
printPostGenerationReport({
|
|
@@ -9290,15 +9556,15 @@ ${pagesCtx}`
|
|
|
9290
9556
|
const issues = validatePageQuality(codeToWrite);
|
|
9291
9557
|
const report = formatIssues(issues);
|
|
9292
9558
|
if (report) {
|
|
9293
|
-
console.log(
|
|
9559
|
+
console.log(chalk12.yellow(`
|
|
9294
9560
|
\u{1F50D} Quality check for ${pageDef.name || pageDef.id}:`));
|
|
9295
|
-
console.log(
|
|
9561
|
+
console.log(chalk12.dim(report));
|
|
9296
9562
|
}
|
|
9297
9563
|
const consistency = checkDesignConsistency(codeToWrite);
|
|
9298
9564
|
if (consistency.length > 0) {
|
|
9299
|
-
console.log(
|
|
9565
|
+
console.log(chalk12.yellow(`
|
|
9300
9566
|
\u{1F3A8} Design consistency for ${pageDef.name || pageDef.id}:`));
|
|
9301
|
-
consistency.forEach((w) => console.log(
|
|
9567
|
+
consistency.forEach((w) => console.log(chalk12.dim(` \u26A0 [${w.type}] ${w.message}`)));
|
|
9302
9568
|
}
|
|
9303
9569
|
} else {
|
|
9304
9570
|
try {
|
|
@@ -9307,8 +9573,8 @@ ${pagesCtx}`
|
|
|
9307
9573
|
if (fixes.length > 0) {
|
|
9308
9574
|
code = fixed;
|
|
9309
9575
|
await writeFile(absPath, code);
|
|
9310
|
-
console.log(
|
|
9311
|
-
fixes.forEach((f) => console.log(
|
|
9576
|
+
console.log(chalk12.dim(" \u{1F527} Auto-fixes applied:"));
|
|
9577
|
+
fixes.forEach((f) => console.log(chalk12.dim(` ${f}`)));
|
|
9312
9578
|
}
|
|
9313
9579
|
const relFilePath = routeToRelPath(route, isAuth);
|
|
9314
9580
|
const manifest = await loadManifest6(projectRoot);
|
|
@@ -9325,9 +9591,9 @@ ${pagesCtx}`
|
|
|
9325
9591
|
const issues = validatePageQuality(code);
|
|
9326
9592
|
const report = formatIssues(issues);
|
|
9327
9593
|
if (report) {
|
|
9328
|
-
console.log(
|
|
9594
|
+
console.log(chalk12.yellow(`
|
|
9329
9595
|
\u{1F50D} Quality check for ${pageDef.name || pageDef.id}:`));
|
|
9330
|
-
console.log(
|
|
9596
|
+
console.log(chalk12.dim(report));
|
|
9331
9597
|
}
|
|
9332
9598
|
} catch {
|
|
9333
9599
|
}
|
|
@@ -9450,7 +9716,7 @@ function hasNavChanged(before, after) {
|
|
|
9450
9716
|
}
|
|
9451
9717
|
|
|
9452
9718
|
// src/commands/chat/interactive.ts
|
|
9453
|
-
import
|
|
9719
|
+
import chalk13 from "chalk";
|
|
9454
9720
|
import { resolve as resolve8 } from "path";
|
|
9455
9721
|
import { existsSync as existsSync15, readFileSync as readFileSync10, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5 } from "fs";
|
|
9456
9722
|
import { DesignSystemManager as DesignSystemManager6, ComponentManager as ComponentManager4, loadManifest as loadManifest7 } from "@getcoherent/core";
|
|
@@ -9468,7 +9734,7 @@ async function interactiveChat(options, chatCommandFn) {
|
|
|
9468
9734
|
const validProviders = ["claude", "openai", "auto"];
|
|
9469
9735
|
const provider = (options.provider || "auto").toLowerCase();
|
|
9470
9736
|
if (!validProviders.includes(provider)) {
|
|
9471
|
-
console.error(
|
|
9737
|
+
console.error(chalk13.red(`
|
|
9472
9738
|
\u274C Invalid provider: ${options.provider}`));
|
|
9473
9739
|
process.exit(1);
|
|
9474
9740
|
}
|
|
@@ -9483,13 +9749,13 @@ async function interactiveChat(options, chatCommandFn) {
|
|
|
9483
9749
|
} catch (e) {
|
|
9484
9750
|
if (DEBUG3) console.error("Failed to load REPL history:", e);
|
|
9485
9751
|
}
|
|
9486
|
-
console.log(
|
|
9487
|
-
console.log(
|
|
9488
|
-
console.log(
|
|
9752
|
+
console.log(chalk13.cyan("\n\u{1F3A8} Coherent Interactive Mode"));
|
|
9753
|
+
console.log(chalk13.dim(" Type your requests, or use built-in commands."));
|
|
9754
|
+
console.log(chalk13.dim(' Type "help" for available commands, "exit" to quit.\n'));
|
|
9489
9755
|
const rl = createInterface({
|
|
9490
9756
|
input: process.stdin,
|
|
9491
9757
|
output: process.stdout,
|
|
9492
|
-
prompt:
|
|
9758
|
+
prompt: chalk13.cyan("Coherent> "),
|
|
9493
9759
|
history,
|
|
9494
9760
|
historySize: 200
|
|
9495
9761
|
});
|
|
@@ -9503,37 +9769,37 @@ async function interactiveChat(options, chatCommandFn) {
|
|
|
9503
9769
|
const lower = input.toLowerCase();
|
|
9504
9770
|
if (lower === "exit" || lower === "quit" || lower === "q") {
|
|
9505
9771
|
saveHistory();
|
|
9506
|
-
console.log(
|
|
9772
|
+
console.log(chalk13.dim("\nBye!\n"));
|
|
9507
9773
|
rl.close();
|
|
9508
9774
|
process.exit(0);
|
|
9509
9775
|
}
|
|
9510
9776
|
if (lower === "help") {
|
|
9511
|
-
console.log(
|
|
9512
|
-
console.log(
|
|
9513
|
-
console.log(
|
|
9514
|
-
console.log(
|
|
9515
|
-
console.log(
|
|
9516
|
-
console.log(
|
|
9517
|
-
console.log(
|
|
9518
|
-
console.log(
|
|
9519
|
-
console.log(
|
|
9520
|
-
console.log(
|
|
9521
|
-
console.log(
|
|
9777
|
+
console.log(chalk13.bold("\n Built-in commands:"));
|
|
9778
|
+
console.log(chalk13.white(" components") + chalk13.dim(" \u2014 list shared and UI components"));
|
|
9779
|
+
console.log(chalk13.white(" pages") + chalk13.dim(" \u2014 list all pages"));
|
|
9780
|
+
console.log(chalk13.white(" tokens") + chalk13.dim(" \u2014 show design tokens"));
|
|
9781
|
+
console.log(chalk13.white(" status") + chalk13.dim(" \u2014 project summary"));
|
|
9782
|
+
console.log(chalk13.white(" help") + chalk13.dim(" \u2014 this help"));
|
|
9783
|
+
console.log(chalk13.white(" exit") + chalk13.dim(" \u2014 quit interactive mode"));
|
|
9784
|
+
console.log(chalk13.bold("\n Target shortcuts:"));
|
|
9785
|
+
console.log(chalk13.white(" @ComponentName <msg>") + chalk13.dim(" \u2014 target a shared component"));
|
|
9786
|
+
console.log(chalk13.white(" @/route <msg>") + chalk13.dim(" \u2014 target a page by route"));
|
|
9787
|
+
console.log(chalk13.dim("\n Anything else is sent to AI as a modification request.\n"));
|
|
9522
9788
|
rl.prompt();
|
|
9523
9789
|
return;
|
|
9524
9790
|
}
|
|
9525
9791
|
if (lower === "components" || lower === "list components" || lower.includes("what components")) {
|
|
9526
9792
|
const manifest = await loadManifest7(projectRoot);
|
|
9527
9793
|
if (manifest.shared.length === 0) {
|
|
9528
|
-
console.log(
|
|
9794
|
+
console.log(chalk13.gray("\n No shared components yet.\n"));
|
|
9529
9795
|
} else {
|
|
9530
9796
|
console.log("");
|
|
9531
9797
|
const order = { layout: 0, section: 1, widget: 2 };
|
|
9532
9798
|
const sorted = [...manifest.shared].sort((a, b) => (order[a.type] ?? 9) - (order[b.type] ?? 9));
|
|
9533
9799
|
sorted.forEach((entry) => {
|
|
9534
|
-
const usage = entry.usedIn.includes("app/layout.tsx") ?
|
|
9800
|
+
const usage = entry.usedIn.includes("app/layout.tsx") ? chalk13.green("all pages") : entry.usedIn.length > 0 ? chalk13.gray(entry.usedIn.join(", ")) : chalk13.gray("unused");
|
|
9535
9801
|
console.log(
|
|
9536
|
-
` ${
|
|
9802
|
+
` ${chalk13.cyan(entry.id.padEnd(8))} ${chalk13.white(entry.name.padEnd(18))} ${chalk13.gray(entry.type.padEnd(9))} ${usage}`
|
|
9537
9803
|
);
|
|
9538
9804
|
});
|
|
9539
9805
|
console.log("");
|
|
@@ -9544,11 +9810,11 @@ async function interactiveChat(options, chatCommandFn) {
|
|
|
9544
9810
|
if (lower === "pages" || lower === "list pages" || lower.includes("what pages")) {
|
|
9545
9811
|
const currentConfig = dsm.getConfig();
|
|
9546
9812
|
if (currentConfig.pages.length === 0) {
|
|
9547
|
-
console.log(
|
|
9813
|
+
console.log(chalk13.gray("\n No pages yet.\n"));
|
|
9548
9814
|
} else {
|
|
9549
9815
|
console.log("");
|
|
9550
9816
|
currentConfig.pages.forEach((p) => {
|
|
9551
|
-
console.log(` ${
|
|
9817
|
+
console.log(` ${chalk13.white(p.name.padEnd(22))} ${chalk13.gray(p.route)}`);
|
|
9552
9818
|
});
|
|
9553
9819
|
console.log("");
|
|
9554
9820
|
}
|
|
@@ -9558,10 +9824,10 @@ async function interactiveChat(options, chatCommandFn) {
|
|
|
9558
9824
|
if (lower === "status") {
|
|
9559
9825
|
const currentConfig = dsm.getConfig();
|
|
9560
9826
|
const manifest = await loadManifest7(projectRoot);
|
|
9561
|
-
console.log(
|
|
9827
|
+
console.log(chalk13.bold(`
|
|
9562
9828
|
${currentConfig.name || "Coherent Project"}`));
|
|
9563
9829
|
console.log(
|
|
9564
|
-
|
|
9830
|
+
chalk13.dim(
|
|
9565
9831
|
` Pages: ${currentConfig.pages.length} | Shared components: ${manifest.shared.length} | UI components: ${cm.getAllComponents().length}
|
|
9566
9832
|
`
|
|
9567
9833
|
)
|
|
@@ -9572,21 +9838,21 @@ async function interactiveChat(options, chatCommandFn) {
|
|
|
9572
9838
|
if (lower === "tokens" || lower === "show tokens" || lower === "design tokens") {
|
|
9573
9839
|
const currentConfig = dsm.getConfig();
|
|
9574
9840
|
const t = currentConfig.tokens;
|
|
9575
|
-
console.log(
|
|
9576
|
-
console.log(
|
|
9841
|
+
console.log(chalk13.bold("\n Design Tokens\n"));
|
|
9842
|
+
console.log(chalk13.cyan(" Colors (light)"));
|
|
9577
9843
|
for (const [k, v] of Object.entries(t.colors.light)) {
|
|
9578
|
-
console.log(` ${
|
|
9844
|
+
console.log(` ${chalk13.white(k.padEnd(14))} ${chalk13.gray(v)}`);
|
|
9579
9845
|
}
|
|
9580
|
-
console.log(
|
|
9581
|
-
console.log(` ${
|
|
9582
|
-
console.log(` ${
|
|
9583
|
-
console.log(
|
|
9846
|
+
console.log(chalk13.cyan("\n Typography"));
|
|
9847
|
+
console.log(` ${chalk13.white("sans".padEnd(14))} ${chalk13.gray(t.typography.fontFamily.sans)}`);
|
|
9848
|
+
console.log(` ${chalk13.white("mono".padEnd(14))} ${chalk13.gray(t.typography.fontFamily.mono)}`);
|
|
9849
|
+
console.log(chalk13.cyan("\n Spacing"));
|
|
9584
9850
|
for (const [k, v] of Object.entries(t.spacing)) {
|
|
9585
|
-
console.log(` ${
|
|
9851
|
+
console.log(` ${chalk13.white(k.padEnd(14))} ${chalk13.gray(v)}`);
|
|
9586
9852
|
}
|
|
9587
|
-
console.log(
|
|
9853
|
+
console.log(chalk13.cyan("\n Radius"));
|
|
9588
9854
|
for (const [k, v] of Object.entries(t.radius)) {
|
|
9589
|
-
console.log(` ${
|
|
9855
|
+
console.log(` ${chalk13.white(k.padEnd(14))} ${chalk13.gray(v)}`);
|
|
9590
9856
|
}
|
|
9591
9857
|
console.log("");
|
|
9592
9858
|
rl.prompt();
|
|
@@ -9608,7 +9874,7 @@ async function interactiveChat(options, chatCommandFn) {
|
|
|
9608
9874
|
await dsm.load();
|
|
9609
9875
|
} catch (err) {
|
|
9610
9876
|
if (!err._printed) {
|
|
9611
|
-
console.error(
|
|
9877
|
+
console.error(chalk13.red(`
|
|
9612
9878
|
Error: ${err.message}
|
|
9613
9879
|
`));
|
|
9614
9880
|
}
|
|
@@ -9640,8 +9906,8 @@ async function chatCommand(message, options) {
|
|
|
9640
9906
|
process.exit(1);
|
|
9641
9907
|
};
|
|
9642
9908
|
if (!message) {
|
|
9643
|
-
console.error(
|
|
9644
|
-
console.log(
|
|
9909
|
+
console.error(chalk14.red('\n\u274C No message provided. Use: coherent chat "your request"\n'));
|
|
9910
|
+
console.log(chalk14.dim(" Or use interactive mode: coherent chat -i\n"));
|
|
9645
9911
|
bail("No message provided");
|
|
9646
9912
|
}
|
|
9647
9913
|
const spinner = ora2("Processing your request...").start();
|
|
@@ -9651,7 +9917,7 @@ async function chatCommand(message, options) {
|
|
|
9651
9917
|
const migrationGuard = join11(projectRoot, ".coherent", "migration-in-progress");
|
|
9652
9918
|
if (existsSync16(migrationGuard)) {
|
|
9653
9919
|
spinner.fail("Migration in progress");
|
|
9654
|
-
console.error(
|
|
9920
|
+
console.error(chalk14.red("\n\u274C A migration is in progress. Run `coherent migrate --rollback` to undo first."));
|
|
9655
9921
|
bail("Migration in progress");
|
|
9656
9922
|
}
|
|
9657
9923
|
const validProviders = ["claude", "openai", "auto"];
|
|
@@ -9661,20 +9927,20 @@ async function chatCommand(message, options) {
|
|
|
9661
9927
|
releaseLock = await acquireProjectLock(projectRoot);
|
|
9662
9928
|
if (!validProviders.includes(provider)) {
|
|
9663
9929
|
spinner.fail("Invalid provider");
|
|
9664
|
-
console.error(
|
|
9930
|
+
console.error(chalk14.red(`
|
|
9665
9931
|
\u274C Invalid provider: ${options.provider}`));
|
|
9666
|
-
console.log(
|
|
9932
|
+
console.log(chalk14.dim(`Valid options: ${validProviders.join(", ")}`));
|
|
9667
9933
|
bail(`Invalid provider: ${options.provider}`);
|
|
9668
9934
|
}
|
|
9669
9935
|
spinner.text = "Loading design system configuration...";
|
|
9670
9936
|
const config2 = await loadConfig(configPath);
|
|
9671
9937
|
if (config2.coherentVersion && config2.coherentVersion !== CLI_VERSION2) {
|
|
9672
9938
|
spinner.stop();
|
|
9673
|
-
console.log(
|
|
9674
|
-
console.log(
|
|
9675
|
-
console.log(
|
|
9676
|
-
console.log(
|
|
9677
|
-
console.log(
|
|
9939
|
+
console.log(chalk14.yellow("\n\u26A0\uFE0F Version mismatch detected\n"));
|
|
9940
|
+
console.log(chalk14.gray(" Project created with: ") + chalk14.white(`v${config2.coherentVersion}`));
|
|
9941
|
+
console.log(chalk14.gray(" Current CLI version: ") + chalk14.white(`v${CLI_VERSION2}`));
|
|
9942
|
+
console.log(chalk14.cyan("\n \u{1F4A1} Run `coherent update` to apply latest changes to your project.\n"));
|
|
9943
|
+
console.log(chalk14.dim(" Continuing anyway...\n"));
|
|
9678
9944
|
spinner.start("Loading design system configuration...");
|
|
9679
9945
|
}
|
|
9680
9946
|
if (needsGlobalsFix(projectRoot)) {
|
|
@@ -9701,9 +9967,9 @@ async function chatCommand(message, options) {
|
|
|
9701
9967
|
const done = await setDefaultDarkTheme(projectRoot);
|
|
9702
9968
|
spinner.stop();
|
|
9703
9969
|
if (done) {
|
|
9704
|
-
console.log(
|
|
9970
|
+
console.log(chalk14.green("\n\u2705 Default theme set to dark. Reload the app to see changes.\n"));
|
|
9705
9971
|
} else {
|
|
9706
|
-
console.log(
|
|
9972
|
+
console.log(chalk14.yellow("\n\u26A0\uFE0F Could not update layout (app/layout.tsx not found).\n"));
|
|
9707
9973
|
}
|
|
9708
9974
|
return;
|
|
9709
9975
|
}
|
|
@@ -9719,10 +9985,10 @@ async function chatCommand(message, options) {
|
|
|
9719
9985
|
dsm.updateConfig(cfg);
|
|
9720
9986
|
dsm.save();
|
|
9721
9987
|
spinner.stop();
|
|
9722
|
-
console.log(
|
|
9988
|
+
console.log(chalk14.green("\n\u2705 Default theme set to light. Reload the app to see changes.\n"));
|
|
9723
9989
|
} catch {
|
|
9724
9990
|
spinner.stop();
|
|
9725
|
-
console.log(
|
|
9991
|
+
console.log(chalk14.yellow("\n\u26A0\uFE0F Could not update layout (app/layout.tsx not found).\n"));
|
|
9726
9992
|
}
|
|
9727
9993
|
return;
|
|
9728
9994
|
}
|
|
@@ -9732,7 +9998,7 @@ async function chatCommand(message, options) {
|
|
|
9732
9998
|
const { created, id } = await ensureThemeToggle(projectRoot);
|
|
9733
9999
|
spinner.stop();
|
|
9734
10000
|
console.log(
|
|
9735
|
-
|
|
10001
|
+
chalk14.green(
|
|
9736
10002
|
`
|
|
9737
10003
|
\u2705 ${created ? `Created ${id} (ThemeToggle) and added to layout` : "ThemeToggle already present; layout updated"}.
|
|
9738
10004
|
`
|
|
@@ -9740,7 +10006,7 @@ async function chatCommand(message, options) {
|
|
|
9740
10006
|
);
|
|
9741
10007
|
} catch (e) {
|
|
9742
10008
|
spinner.fail("Failed to add theme toggle");
|
|
9743
|
-
if (e instanceof Error) console.error(
|
|
10009
|
+
if (e instanceof Error) console.error(chalk14.red("\n\u274C " + e.message + "\n"));
|
|
9744
10010
|
}
|
|
9745
10011
|
return;
|
|
9746
10012
|
}
|
|
@@ -9756,12 +10022,12 @@ async function chatCommand(message, options) {
|
|
|
9756
10022
|
manifest = { ...manifest, shared: validShared };
|
|
9757
10023
|
await saveManifest2(project.root, manifest);
|
|
9758
10024
|
if (DEBUG4) {
|
|
9759
|
-
console.log(
|
|
10025
|
+
console.log(chalk14.dim(`[pre-gen] Cleaned ${cleaned} orphaned component(s) from manifest`));
|
|
9760
10026
|
}
|
|
9761
10027
|
}
|
|
9762
10028
|
const sharedComponentsSummary = buildSharedComponentsSummary(manifest);
|
|
9763
10029
|
if (DEBUG4 && sharedComponentsSummary) {
|
|
9764
|
-
console.log(
|
|
10030
|
+
console.log(chalk14.dim("[add-page] sharedComponentsSummary in prompt:\n" + sharedComponentsSummary));
|
|
9765
10031
|
}
|
|
9766
10032
|
let requests;
|
|
9767
10033
|
let uxRecommendations;
|
|
@@ -9773,7 +10039,12 @@ async function chatCommand(message, options) {
|
|
|
9773
10039
|
) || []).length >= SPLIT_THRESHOLD;
|
|
9774
10040
|
if (multiPageHint) {
|
|
9775
10041
|
try {
|
|
9776
|
-
|
|
10042
|
+
const splitResult = await splitGeneratePages(spinner, message, modCtx, provider, parseOpts);
|
|
10043
|
+
requests = splitResult.requests;
|
|
10044
|
+
if (splitResult.plan && projectRoot) {
|
|
10045
|
+
savePlan(projectRoot, splitResult.plan);
|
|
10046
|
+
await ensurePlanGroupLayouts(projectRoot, splitResult.plan);
|
|
10047
|
+
}
|
|
9777
10048
|
uxRecommendations = void 0;
|
|
9778
10049
|
} catch {
|
|
9779
10050
|
spinner.warn("Split generation encountered an issue \u2014 trying page-by-page...");
|
|
@@ -9846,7 +10117,12 @@ async function chatCommand(message, options) {
|
|
|
9846
10117
|
if (isTruncated || isJsonError) {
|
|
9847
10118
|
spinner.warn("Response too large \u2014 splitting into smaller requests...");
|
|
9848
10119
|
try {
|
|
9849
|
-
|
|
10120
|
+
const splitResult = await splitGeneratePages(spinner, message, modCtx, provider, parseOpts);
|
|
10121
|
+
requests = splitResult.requests;
|
|
10122
|
+
if (splitResult.plan && projectRoot) {
|
|
10123
|
+
savePlan(projectRoot, splitResult.plan);
|
|
10124
|
+
await ensurePlanGroupLayouts(projectRoot, splitResult.plan);
|
|
10125
|
+
}
|
|
9850
10126
|
uxRecommendations = void 0;
|
|
9851
10127
|
} catch {
|
|
9852
10128
|
throw firstError;
|
|
@@ -9858,10 +10134,10 @@ async function chatCommand(message, options) {
|
|
|
9858
10134
|
}
|
|
9859
10135
|
if (requests.length === 0) {
|
|
9860
10136
|
spinner.fail("No modifications found in your request");
|
|
9861
|
-
console.log(
|
|
9862
|
-
console.log(
|
|
9863
|
-
console.log(
|
|
9864
|
-
console.log(
|
|
10137
|
+
console.log(chalk14.yellow("\n\u{1F4A1} Try being more specific, e.g.:"));
|
|
10138
|
+
console.log(chalk14.dim(' - "make buttons blue"'));
|
|
10139
|
+
console.log(chalk14.dim(' - "add a pricing page"'));
|
|
10140
|
+
console.log(chalk14.dim(' - "change primary color to green"'));
|
|
9865
10141
|
return;
|
|
9866
10142
|
}
|
|
9867
10143
|
spinner.succeed(`Parsed ${requests.length} modification(s)`);
|
|
@@ -9869,11 +10145,11 @@ async function chatCommand(message, options) {
|
|
|
9869
10145
|
normalizedRequests = normalizedRequests.map((req) => {
|
|
9870
10146
|
const result = normalizeRequest(req, dsm.getConfig());
|
|
9871
10147
|
if ("error" in result) {
|
|
9872
|
-
console.log(
|
|
10148
|
+
console.log(chalk14.yellow(` \u26A0 Skipped: ${result.error}`));
|
|
9873
10149
|
return null;
|
|
9874
10150
|
}
|
|
9875
10151
|
if (result.type !== req.type) {
|
|
9876
|
-
console.log(
|
|
10152
|
+
console.log(chalk14.dim(` \u2139 Adjusted: ${req.type} \u2192 ${result.type} (target: ${req.target})`));
|
|
9877
10153
|
}
|
|
9878
10154
|
return result;
|
|
9879
10155
|
}).filter((r) => r !== null);
|
|
@@ -9929,13 +10205,13 @@ async function chatCommand(message, options) {
|
|
|
9929
10205
|
}
|
|
9930
10206
|
}
|
|
9931
10207
|
if (DEBUG4) {
|
|
9932
|
-
console.log(
|
|
10208
|
+
console.log(chalk14.gray(`
|
|
9933
10209
|
[DEBUG] Pre-flight analysis for page "${page.name || page.route}": `));
|
|
9934
|
-
console.log(
|
|
10210
|
+
console.log(chalk14.gray(` Page sections: ${page.sections?.length || 0}`));
|
|
9935
10211
|
if (page.sections?.[0]?.props?.fields) {
|
|
9936
|
-
console.log(
|
|
10212
|
+
console.log(chalk14.gray(` First section has ${page.sections[0].props.fields.length} fields`));
|
|
9937
10213
|
page.sections[0].props.fields.forEach((f, i) => {
|
|
9938
|
-
console.log(
|
|
10214
|
+
console.log(chalk14.gray(` Field ${i}: component=${f.component}`));
|
|
9939
10215
|
});
|
|
9940
10216
|
}
|
|
9941
10217
|
}
|
|
@@ -9958,8 +10234,8 @@ async function chatCommand(message, options) {
|
|
|
9958
10234
|
const INVALID_COMPONENT_IDS = /* @__PURE__ */ new Set(["ui", "shared", "lib", "utils", "hooks", "app", "components"]);
|
|
9959
10235
|
for (const id of INVALID_COMPONENT_IDS) allNeededComponentIds.delete(id);
|
|
9960
10236
|
if (DEBUG4) {
|
|
9961
|
-
console.log(
|
|
9962
|
-
console.log(
|
|
10237
|
+
console.log(chalk14.gray("\n[DEBUG] Pre-flight analysis (consolidated):"));
|
|
10238
|
+
console.log(chalk14.gray(` All needed components: ${Array.from(allNeededComponentIds).join(", ")}`));
|
|
9963
10239
|
console.log("");
|
|
9964
10240
|
}
|
|
9965
10241
|
const missingComponents = [];
|
|
@@ -9967,59 +10243,59 @@ async function chatCommand(message, options) {
|
|
|
9967
10243
|
const isRegistered = !!cm.read(componentId);
|
|
9968
10244
|
const filePath = join11(projectRoot, "components", "ui", `${componentId}.tsx`);
|
|
9969
10245
|
const fileExists = existsSync16(filePath);
|
|
9970
|
-
if (DEBUG4) console.log(
|
|
10246
|
+
if (DEBUG4) console.log(chalk14.gray(` Checking ${componentId}: registered=${isRegistered} file=${fileExists}`));
|
|
9971
10247
|
if (!isRegistered || !fileExists) {
|
|
9972
10248
|
missingComponents.push(componentId);
|
|
9973
10249
|
}
|
|
9974
10250
|
}
|
|
9975
10251
|
if (missingComponents.length > 0) {
|
|
9976
10252
|
spinner.stop();
|
|
9977
|
-
console.log(
|
|
10253
|
+
console.log(chalk14.cyan("\n\u{1F50D} Pre-flight check: Installing missing components...\n"));
|
|
9978
10254
|
const provider2 = getComponentProvider();
|
|
9979
10255
|
for (const componentId of missingComponents) {
|
|
9980
10256
|
if (DEBUG4) {
|
|
9981
|
-
console.log(
|
|
9982
|
-
console.log(
|
|
10257
|
+
console.log(chalk14.gray(` [DEBUG] Trying to install: ${componentId}`));
|
|
10258
|
+
console.log(chalk14.gray(` [DEBUG] provider.has(${componentId}): ${provider2.has(componentId)}`));
|
|
9983
10259
|
}
|
|
9984
10260
|
if (provider2.has(componentId)) {
|
|
9985
10261
|
try {
|
|
9986
10262
|
const result = await provider2.installComponent(componentId, projectRoot);
|
|
9987
|
-
if (DEBUG4) console.log(
|
|
10263
|
+
if (DEBUG4) console.log(chalk14.gray(` [DEBUG] installComponent result: ${result.success}`));
|
|
9988
10264
|
if (result.success && result.componentDef) {
|
|
9989
10265
|
if (!cm.read(componentId)) {
|
|
9990
10266
|
if (DEBUG4)
|
|
9991
10267
|
console.log(
|
|
9992
|
-
|
|
10268
|
+
chalk14.gray(` [DEBUG] Registering ${result.componentDef.id} (${result.componentDef.name})`)
|
|
9993
10269
|
);
|
|
9994
10270
|
const regResult = await cm.register(result.componentDef);
|
|
9995
10271
|
if (DEBUG4) {
|
|
9996
10272
|
console.log(
|
|
9997
|
-
|
|
10273
|
+
chalk14.gray(
|
|
9998
10274
|
` [DEBUG] Register result: ${regResult.success ? "SUCCESS" : "FAILED"}${!regResult.success && regResult.message ? ` - ${regResult.message}` : ""}`
|
|
9999
10275
|
)
|
|
10000
10276
|
);
|
|
10001
10277
|
}
|
|
10002
10278
|
if (regResult.success) {
|
|
10003
10279
|
preflightInstalledIds.push(result.componentDef.id);
|
|
10004
|
-
console.log(
|
|
10280
|
+
console.log(chalk14.green(` \u2728 Auto-installed ${result.componentDef.name} component`));
|
|
10005
10281
|
dsm.updateConfig(regResult.config);
|
|
10006
10282
|
cm.updateConfig(regResult.config);
|
|
10007
10283
|
pm.updateConfig(regResult.config);
|
|
10008
10284
|
}
|
|
10009
10285
|
} else {
|
|
10010
10286
|
preflightInstalledIds.push(result.componentDef.id);
|
|
10011
|
-
console.log(
|
|
10287
|
+
console.log(chalk14.green(` \u2728 Re-installed ${result.componentDef.name} component (file was missing)`));
|
|
10012
10288
|
}
|
|
10013
10289
|
}
|
|
10014
10290
|
} catch (error) {
|
|
10015
|
-
console.log(
|
|
10016
|
-
console.log(
|
|
10291
|
+
console.log(chalk14.red(` \u274C Failed to install ${componentId}:`));
|
|
10292
|
+
console.log(chalk14.red(` ${error instanceof Error ? error.message : error}`));
|
|
10017
10293
|
if (error instanceof Error && error.stack) {
|
|
10018
|
-
console.log(
|
|
10294
|
+
console.log(chalk14.gray(` ${error.stack.split("\n")[1]}`));
|
|
10019
10295
|
}
|
|
10020
10296
|
}
|
|
10021
10297
|
} else {
|
|
10022
|
-
console.log(
|
|
10298
|
+
console.log(chalk14.yellow(` \u26A0\uFE0F Component ${componentId} not available`));
|
|
10023
10299
|
}
|
|
10024
10300
|
}
|
|
10025
10301
|
console.log("");
|
|
@@ -10030,11 +10306,11 @@ async function chatCommand(message, options) {
|
|
|
10030
10306
|
const toInstallNpm = [...neededPkgs].filter((p) => !installedPkgs.has(p));
|
|
10031
10307
|
if (toInstallNpm.length > 0) {
|
|
10032
10308
|
spinner.stop();
|
|
10033
|
-
console.log(
|
|
10309
|
+
console.log(chalk14.cyan(`
|
|
10034
10310
|
\u{1F4E6} Auto-installing missing dependencies: ${toInstallNpm.join(", ")}
|
|
10035
10311
|
`));
|
|
10036
10312
|
const ok = await installPackages(projectRoot, toInstallNpm);
|
|
10037
|
-
if (!ok) console.log(
|
|
10313
|
+
if (!ok) console.log(chalk14.yellow(` Run manually: npm install ${toInstallNpm.join(" ")}
|
|
10038
10314
|
`));
|
|
10039
10315
|
spinner.start("Applying modifications...");
|
|
10040
10316
|
}
|
|
@@ -10045,7 +10321,7 @@ async function chatCommand(message, options) {
|
|
|
10045
10321
|
if (componentId && preflightComponentIds.has(componentId)) {
|
|
10046
10322
|
if (DEBUG4) {
|
|
10047
10323
|
console.log(
|
|
10048
|
-
|
|
10324
|
+
chalk14.gray(`[DEBUG] Filtered duplicate add-component: ${componentId} (already installed in pre-flight)`)
|
|
10049
10325
|
);
|
|
10050
10326
|
}
|
|
10051
10327
|
return false;
|
|
@@ -10054,11 +10330,11 @@ async function chatCommand(message, options) {
|
|
|
10054
10330
|
return true;
|
|
10055
10331
|
});
|
|
10056
10332
|
if (DEBUG4 && preflightComponentIds.size > 0) {
|
|
10057
|
-
console.log(
|
|
10333
|
+
console.log(chalk14.gray(`[DEBUG] Remaining requests after filtering: ${normalizedRequests.length}`));
|
|
10058
10334
|
}
|
|
10059
10335
|
try {
|
|
10060
10336
|
createBackup(projectRoot);
|
|
10061
|
-
if (DEBUG4) console.log(
|
|
10337
|
+
if (DEBUG4) console.log(chalk14.dim("[backup] Created snapshot"));
|
|
10062
10338
|
} catch {
|
|
10063
10339
|
}
|
|
10064
10340
|
const navBefore = takeNavSnapshot(
|
|
@@ -10113,10 +10389,10 @@ async function chatCommand(message, options) {
|
|
|
10113
10389
|
const SCAFFOLD_AI_LIMIT = 10;
|
|
10114
10390
|
if (missingRoutes.length > 0 && missingRoutes.length <= SCAFFOLD_AI_LIMIT) {
|
|
10115
10391
|
spinner.stop();
|
|
10116
|
-
console.log(
|
|
10392
|
+
console.log(chalk14.cyan(`
|
|
10117
10393
|
\u{1F517} Auto-scaffolding ${missingRoutes.length} linked page(s)...`));
|
|
10118
10394
|
console.log(
|
|
10119
|
-
|
|
10395
|
+
chalk14.dim(
|
|
10120
10396
|
` (${missingRoutes.length} additional AI call(s) \u2014 disable with settings.autoScaffold: false in config)
|
|
10121
10397
|
`
|
|
10122
10398
|
)
|
|
@@ -10153,7 +10429,7 @@ async function chatCommand(message, options) {
|
|
|
10153
10429
|
}
|
|
10154
10430
|
} catch (err) {
|
|
10155
10431
|
scaffoldSpinner.warn(` Could not scaffold "${pageName}" (${linkedRoute}) \u2014 skipped`);
|
|
10156
|
-
if (DEBUG4) console.log(
|
|
10432
|
+
if (DEBUG4) console.log(chalk14.dim(` ${err instanceof Error ? err.message : "unknown error"}`));
|
|
10157
10433
|
}
|
|
10158
10434
|
}
|
|
10159
10435
|
console.log("");
|
|
@@ -10161,7 +10437,7 @@ async function chatCommand(message, options) {
|
|
|
10161
10437
|
} else if (missingRoutes.length > SCAFFOLD_AI_LIMIT) {
|
|
10162
10438
|
spinner.stop();
|
|
10163
10439
|
console.log(
|
|
10164
|
-
|
|
10440
|
+
chalk14.yellow(
|
|
10165
10441
|
`
|
|
10166
10442
|
\u26A0 Found ${missingRoutes.length} linked pages \u2014 creating placeholder pages (too many for AI generation).`
|
|
10167
10443
|
)
|
|
@@ -10190,7 +10466,7 @@ async function chatCommand(message, options) {
|
|
|
10190
10466
|
scaffoldedPages.push({ route: linkedRoute, name: `${pageName} (placeholder)` });
|
|
10191
10467
|
}
|
|
10192
10468
|
console.log(
|
|
10193
|
-
|
|
10469
|
+
chalk14.cyan(` Created ${missingRoutes.length} placeholder pages. Use \`coherent chat\` to fill them.
|
|
10194
10470
|
`)
|
|
10195
10471
|
);
|
|
10196
10472
|
spinner.start("Finalizing...");
|
|
@@ -10218,9 +10494,9 @@ async function chatCommand(message, options) {
|
|
|
10218
10494
|
}
|
|
10219
10495
|
}
|
|
10220
10496
|
if (linkIssues.length > 0) {
|
|
10221
|
-
console.log(
|
|
10497
|
+
console.log(chalk14.yellow("\n\u{1F517} Broken internal links:"));
|
|
10222
10498
|
for (const { page, message: message2 } of linkIssues) {
|
|
10223
|
-
console.log(
|
|
10499
|
+
console.log(chalk14.dim(` ${page}: ${message2}`));
|
|
10224
10500
|
}
|
|
10225
10501
|
}
|
|
10226
10502
|
}
|
|
@@ -10233,7 +10509,7 @@ async function chatCommand(message, options) {
|
|
|
10233
10509
|
if (latestConfig.theme.defaultMode !== targetMode) {
|
|
10234
10510
|
latestConfig.theme.defaultMode = targetMode;
|
|
10235
10511
|
dsm.updateConfig(latestConfig);
|
|
10236
|
-
if (DEBUG4) console.log(
|
|
10512
|
+
if (DEBUG4) console.log(chalk14.dim(` [theme] Set defaultMode to "${targetMode}"`));
|
|
10237
10513
|
}
|
|
10238
10514
|
const layoutPath = resolve9(projectRoot, "app", "layout.tsx");
|
|
10239
10515
|
try {
|
|
@@ -10241,11 +10517,11 @@ async function chatCommand(message, options) {
|
|
|
10241
10517
|
if (targetMode === "dark" && !layoutCode.includes('className="dark"')) {
|
|
10242
10518
|
layoutCode = layoutCode.replace(/<html\s+lang="en"/, '<html lang="en" className="dark"');
|
|
10243
10519
|
await writeFile(layoutPath, layoutCode);
|
|
10244
|
-
console.log(
|
|
10520
|
+
console.log(chalk14.dim(` \u{1F319} Applied dark theme to layout`));
|
|
10245
10521
|
} else if (targetMode === "light" && layoutCode.includes('className="dark"')) {
|
|
10246
10522
|
layoutCode = layoutCode.replace(' className="dark"', "");
|
|
10247
10523
|
await writeFile(layoutPath, layoutCode);
|
|
10248
|
-
console.log(
|
|
10524
|
+
console.log(chalk14.dim(` \u2600\uFE0F Applied light theme to layout`));
|
|
10249
10525
|
}
|
|
10250
10526
|
} catch {
|
|
10251
10527
|
}
|
|
@@ -10277,7 +10553,7 @@ async function chatCommand(message, options) {
|
|
|
10277
10553
|
}
|
|
10278
10554
|
const finalDeps = await scanAndInstallSharedDeps(projectRoot);
|
|
10279
10555
|
if (finalDeps.length > 0) {
|
|
10280
|
-
console.log(
|
|
10556
|
+
console.log(chalk14.dim(` Auto-installed shared deps: ${finalDeps.join(", ")}`));
|
|
10281
10557
|
}
|
|
10282
10558
|
try {
|
|
10283
10559
|
fixGlobalsCss(projectRoot, updatedConfig);
|
|
@@ -10301,7 +10577,7 @@ async function chatCommand(message, options) {
|
|
|
10301
10577
|
}
|
|
10302
10578
|
await saveHashes(projectRoot, updatedHashes);
|
|
10303
10579
|
} catch {
|
|
10304
|
-
if (DEBUG4) console.log(
|
|
10580
|
+
if (DEBUG4) console.log(chalk14.dim("[hashes] Could not save file hashes"));
|
|
10305
10581
|
}
|
|
10306
10582
|
const successfulPairs = normalizedRequests.map((request, index) => ({ request, result: results[index] })).filter(({ result }) => result.success);
|
|
10307
10583
|
if (successfulPairs.length > 0) {
|
|
@@ -10318,9 +10594,9 @@ async function chatCommand(message, options) {
|
|
|
10318
10594
|
const preflightNames = preflightInstalledIds.map((id) => updatedConfig.components.find((c) => c.id === id)?.name).filter(Boolean);
|
|
10319
10595
|
showPreview(normalizedRequests, results, updatedConfig, preflightNames);
|
|
10320
10596
|
if (scaffoldedPages.length > 0) {
|
|
10321
|
-
console.log(
|
|
10597
|
+
console.log(chalk14.cyan("\u{1F517} Auto-scaffolded linked pages:"));
|
|
10322
10598
|
scaffoldedPages.forEach(({ route, name }) => {
|
|
10323
|
-
console.log(
|
|
10599
|
+
console.log(chalk14.white(` \u2728 ${name} \u2192 ${route}`));
|
|
10324
10600
|
});
|
|
10325
10601
|
console.log("");
|
|
10326
10602
|
}
|
|
@@ -10342,58 +10618,58 @@ ${uxRecommendations}
|
|
|
10342
10618
|
);
|
|
10343
10619
|
}
|
|
10344
10620
|
await appendFile(recPath, section);
|
|
10345
|
-
console.log(
|
|
10621
|
+
console.log(chalk14.cyan("\n\u{1F4CB} UX Recommendations:"));
|
|
10346
10622
|
for (const line of uxRecommendations.split("\n").filter(Boolean)) {
|
|
10347
|
-
console.log(
|
|
10623
|
+
console.log(chalk14.dim(` ${line}`));
|
|
10348
10624
|
}
|
|
10349
|
-
console.log(
|
|
10625
|
+
console.log(chalk14.dim(" \u2192 Saved to /design-system/docs/recommendations"));
|
|
10350
10626
|
} catch (e) {
|
|
10351
10627
|
console.log(
|
|
10352
|
-
|
|
10628
|
+
chalk14.yellow("\n\u26A0\uFE0F Could not write recommendations.md: " + (e instanceof Error ? e.message : String(e)))
|
|
10353
10629
|
);
|
|
10354
|
-
console.log(
|
|
10630
|
+
console.log(chalk14.dim("Recommendations:\n") + uxRecommendations);
|
|
10355
10631
|
}
|
|
10356
10632
|
}
|
|
10357
10633
|
} catch (error) {
|
|
10358
10634
|
spinner.fail("Chat command failed");
|
|
10359
|
-
console.error(
|
|
10635
|
+
console.error(chalk14.red("\n\u2716 Chat command failed"));
|
|
10360
10636
|
const zodError = error;
|
|
10361
10637
|
const issues = zodError.issues || error.errors;
|
|
10362
10638
|
if (issues && Array.isArray(issues)) {
|
|
10363
|
-
console.log(
|
|
10639
|
+
console.log(chalk14.yellow("\n\u26A0\uFE0F AI generated incomplete data. Missing or invalid fields:"));
|
|
10364
10640
|
issues.forEach((err) => {
|
|
10365
|
-
console.log(
|
|
10641
|
+
console.log(chalk14.gray(` \u2022 ${err.path.join(".")}: ${err.message}`));
|
|
10366
10642
|
});
|
|
10367
|
-
console.log(
|
|
10368
|
-
console.log(
|
|
10369
|
-
console.log(
|
|
10643
|
+
console.log(chalk14.cyan("\n\u{1F4A1} Try being more specific, e.g.:"));
|
|
10644
|
+
console.log(chalk14.white(' coherent chat "add a dashboard page with hero section using Button component"'));
|
|
10645
|
+
console.log(chalk14.white(' coherent chat "add pricing page"'));
|
|
10370
10646
|
} else if (error instanceof Error) {
|
|
10371
|
-
console.error(
|
|
10647
|
+
console.error(chalk14.red(error.message));
|
|
10372
10648
|
if (error.message.includes("Unterminated string") || error.message.includes("Unexpected end of JSON") || error.message.includes("Failed to parse modification") && error.message.includes("JSON")) {
|
|
10373
10649
|
console.log(
|
|
10374
|
-
|
|
10650
|
+
chalk14.yellow("\n\u{1F4A1} The AI response was too large or contained invalid JSON. Try splitting your request:")
|
|
10375
10651
|
);
|
|
10376
|
-
console.log(
|
|
10377
|
-
console.log(
|
|
10378
|
-
console.log(
|
|
10652
|
+
console.log(chalk14.white(' coherent chat "add dashboard page with stats and recent activity"'));
|
|
10653
|
+
console.log(chalk14.white(' coherent chat "add account page"'));
|
|
10654
|
+
console.log(chalk14.white(' coherent chat "add settings page"'));
|
|
10379
10655
|
} else if (error.message.includes("API key not found") || error.message.includes("ANTHROPIC_API_KEY") || error.message.includes("OPENAI_API_KEY")) {
|
|
10380
10656
|
const isOpenAI = error.message.includes("OpenAI") || typeof provider !== "undefined" && provider === "openai";
|
|
10381
10657
|
const providerName = isOpenAI ? "OpenAI" : "Anthropic Claude";
|
|
10382
10658
|
const envVar = isOpenAI ? "OPENAI_API_KEY" : "ANTHROPIC_API_KEY";
|
|
10383
10659
|
const url = isOpenAI ? "https://platform.openai.com" : "https://console.anthropic.com";
|
|
10384
|
-
console.log(
|
|
10385
|
-
console.log(
|
|
10386
|
-
console.log(
|
|
10387
|
-
console.log(
|
|
10388
|
-
console.log(
|
|
10389
|
-
console.log(
|
|
10660
|
+
console.log(chalk14.yellow("\n\u{1F4A1} Setup Instructions:"));
|
|
10661
|
+
console.log(chalk14.dim(` 1. Get your ${providerName} API key from: ${url}`));
|
|
10662
|
+
console.log(chalk14.dim(" 2. Create a .env file in the current directory:"));
|
|
10663
|
+
console.log(chalk14.cyan(` echo "${envVar}=your_key_here" > .env`));
|
|
10664
|
+
console.log(chalk14.dim(" 3. Or export it in your shell:"));
|
|
10665
|
+
console.log(chalk14.cyan(` export ${envVar}=your_key_here`));
|
|
10390
10666
|
if (isOpenAI) {
|
|
10391
|
-
console.log(
|
|
10392
|
-
console.log(
|
|
10667
|
+
console.log(chalk14.dim('\n Also ensure "openai" package is installed:'));
|
|
10668
|
+
console.log(chalk14.cyan(" npm install openai"));
|
|
10393
10669
|
}
|
|
10394
10670
|
}
|
|
10395
10671
|
} else {
|
|
10396
|
-
console.error(
|
|
10672
|
+
console.error(chalk14.red("Unknown error occurred"));
|
|
10397
10673
|
}
|
|
10398
10674
|
console.log("");
|
|
10399
10675
|
if (options._throwOnError) {
|
|
@@ -10406,7 +10682,7 @@ ${uxRecommendations}
|
|
|
10406
10682
|
}
|
|
10407
10683
|
|
|
10408
10684
|
// src/commands/preview.ts
|
|
10409
|
-
import
|
|
10685
|
+
import chalk15 from "chalk";
|
|
10410
10686
|
import ora3 from "ora";
|
|
10411
10687
|
import { spawn } from "child_process";
|
|
10412
10688
|
import { existsSync as existsSync19, rmSync as rmSync3, readFileSync as readFileSync14, writeFileSync as writeFileSync10, readdirSync as readdirSync5 } from "fs";
|
|
@@ -10746,13 +11022,13 @@ async function handleFileChange(projectRoot, filePath) {
|
|
|
10746
11022
|
return;
|
|
10747
11023
|
}
|
|
10748
11024
|
const config2 = getWatcherConfig(projectRoot);
|
|
10749
|
-
const
|
|
11025
|
+
const chalk34 = (await import("chalk")).default;
|
|
10750
11026
|
if (config2.autoInstall) {
|
|
10751
11027
|
const missing = findMissingPackagesInCode(content, projectRoot);
|
|
10752
11028
|
if (missing.length > 0) {
|
|
10753
11029
|
const ok = await installPackages(projectRoot, missing);
|
|
10754
11030
|
if (ok) {
|
|
10755
|
-
console.log(
|
|
11031
|
+
console.log(chalk34.cyan(`
|
|
10756
11032
|
\u{1F527} Auto-installed: ${missing.join(", ")} (needed by ${relativePath})`));
|
|
10757
11033
|
}
|
|
10758
11034
|
}
|
|
@@ -10761,12 +11037,12 @@ async function handleFileChange(projectRoot, filePath) {
|
|
|
10761
11037
|
const fixed = sanitizeMetadataStrings(ensureUseClientIfNeeded(content));
|
|
10762
11038
|
if (fixed !== content) {
|
|
10763
11039
|
writeFileSync9(filePath, fixed, "utf-8");
|
|
10764
|
-
console.log(
|
|
11040
|
+
console.log(chalk34.cyan(` \u{1F527} Auto-fixed syntax in ${relativePath}`));
|
|
10765
11041
|
}
|
|
10766
11042
|
}
|
|
10767
11043
|
if (config2.warnNativeElements && hasNativeElements(content)) {
|
|
10768
|
-
console.log(
|
|
10769
|
-
console.log(
|
|
11044
|
+
console.log(chalk34.yellow(` \u26A0 ${relativePath}: uses native HTML elements (<button>, <select>, etc.)`));
|
|
11045
|
+
console.log(chalk34.dim(" Use components from @/components/ui/ instead"));
|
|
10770
11046
|
}
|
|
10771
11047
|
if (config2.warnSharedReuse) {
|
|
10772
11048
|
let manifest;
|
|
@@ -10779,8 +11055,8 @@ async function handleFileChange(projectRoot, filePath) {
|
|
|
10779
11055
|
const dupes = findInlineDuplicatesOfShared(content, manifest);
|
|
10780
11056
|
for (const d of dupes) {
|
|
10781
11057
|
const importPath = d.file.replace(/\.tsx$/, "").replace(/^components\/shared\//, "");
|
|
10782
|
-
console.log(
|
|
10783
|
-
console.log(
|
|
11058
|
+
console.log(chalk34.yellow(` \u26A0 ${relativePath}: has inline code similar to ${d.cid} (${d.name})`));
|
|
11059
|
+
console.log(chalk34.dim(` Consider: import { ${d.name} } from "@/components/shared/${importPath}"`));
|
|
10784
11060
|
}
|
|
10785
11061
|
}
|
|
10786
11062
|
}
|
|
@@ -10789,7 +11065,7 @@ async function handleFileDelete(projectRoot, filePath) {
|
|
|
10789
11065
|
const relativePath = relative4(projectRoot, filePath).replace(/\\/g, "/");
|
|
10790
11066
|
if (!relativePath.startsWith("components/") || relativePath.startsWith("components/ui/")) return;
|
|
10791
11067
|
try {
|
|
10792
|
-
const
|
|
11068
|
+
const chalk34 = (await import("chalk")).default;
|
|
10793
11069
|
const manifest = await loadManifest9(projectRoot);
|
|
10794
11070
|
const orphaned = manifest.shared.find((s) => s.file === relativePath);
|
|
10795
11071
|
if (orphaned) {
|
|
@@ -10798,7 +11074,7 @@ async function handleFileDelete(projectRoot, filePath) {
|
|
|
10798
11074
|
shared: manifest.shared.filter((s) => s.id !== orphaned.id)
|
|
10799
11075
|
};
|
|
10800
11076
|
await saveManifest3(projectRoot, cleaned);
|
|
10801
|
-
console.log(
|
|
11077
|
+
console.log(chalk34.cyan(`
|
|
10802
11078
|
\u{1F5D1} Auto-removed ${orphaned.id} (${orphaned.name}) \u2014 file deleted`));
|
|
10803
11079
|
await writeCursorRules(projectRoot);
|
|
10804
11080
|
}
|
|
@@ -10810,7 +11086,7 @@ async function detectNewComponent(projectRoot, filePath) {
|
|
|
10810
11086
|
if (!relativePath.startsWith("components/") || relativePath.startsWith("components/ui/")) return;
|
|
10811
11087
|
if (!relativePath.endsWith(".tsx") && !relativePath.endsWith(".jsx")) return;
|
|
10812
11088
|
try {
|
|
10813
|
-
const
|
|
11089
|
+
const chalk34 = (await import("chalk")).default;
|
|
10814
11090
|
const manifest = await loadManifest9(projectRoot);
|
|
10815
11091
|
const alreadyRegistered = manifest.shared.some((s) => s.file === relativePath);
|
|
10816
11092
|
if (alreadyRegistered) return;
|
|
@@ -10819,9 +11095,9 @@ async function detectNewComponent(projectRoot, filePath) {
|
|
|
10819
11095
|
if (exports.length > 0) {
|
|
10820
11096
|
const alreadyByName = exports.every((n) => manifest.shared.some((s) => s.name === n));
|
|
10821
11097
|
if (!alreadyByName) {
|
|
10822
|
-
console.log(
|
|
11098
|
+
console.log(chalk34.cyan(`
|
|
10823
11099
|
\u2139 New component detected: ${exports[0]} in ${relativePath}`));
|
|
10824
|
-
console.log(
|
|
11100
|
+
console.log(chalk34.dim(" Register with: coherent sync"));
|
|
10825
11101
|
}
|
|
10826
11102
|
}
|
|
10827
11103
|
} catch {
|
|
@@ -10908,17 +11184,17 @@ function clearStaleCache(projectRoot) {
|
|
|
10908
11184
|
const nextDir = join14(projectRoot, ".next");
|
|
10909
11185
|
if (existsSync19(nextDir)) {
|
|
10910
11186
|
rmSync3(nextDir, { recursive: true, force: true });
|
|
10911
|
-
console.log(
|
|
11187
|
+
console.log(chalk15.dim(" \u2714 Cleared stale build cache"));
|
|
10912
11188
|
}
|
|
10913
11189
|
}
|
|
10914
11190
|
async function preflightDependencyCheck(projectRoot) {
|
|
10915
11191
|
const missing = await findMissingPackages(projectRoot);
|
|
10916
11192
|
if (missing.length === 0) return;
|
|
10917
|
-
console.log(
|
|
11193
|
+
console.log(chalk15.cyan(`
|
|
10918
11194
|
Auto-installing missing dependencies: ${missing.join(", ")}`));
|
|
10919
11195
|
const ok = await installPackages(projectRoot, missing);
|
|
10920
|
-
if (ok) console.log(
|
|
10921
|
-
else console.log(
|
|
11196
|
+
if (ok) console.log(chalk15.dim(" \u2714 Installed"));
|
|
11197
|
+
else console.log(chalk15.yellow(` Run manually: npm install ${missing.join(" ")}`));
|
|
10922
11198
|
}
|
|
10923
11199
|
async function listPageFiles(appDir) {
|
|
10924
11200
|
const out = [];
|
|
@@ -10942,7 +11218,7 @@ async function validateSyntax(projectRoot) {
|
|
|
10942
11218
|
const fixed = fixUnescapedLtInJsx(sanitizeMetadataStrings(ensureUseClientIfNeeded(content)));
|
|
10943
11219
|
if (fixed !== content) {
|
|
10944
11220
|
writeFileSync10(file, fixed, "utf-8");
|
|
10945
|
-
console.log(
|
|
11221
|
+
console.log(chalk15.dim(` \u2714 Auto-fixed syntax: ${file.replace(projectRoot, ".").replace(/^\.[/\\]/, "")}`));
|
|
10946
11222
|
}
|
|
10947
11223
|
}
|
|
10948
11224
|
}
|
|
@@ -10984,7 +11260,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
10984
11260
|
try {
|
|
10985
11261
|
const result = await provider.installComponent(componentId, projectRoot);
|
|
10986
11262
|
if (result.success) {
|
|
10987
|
-
console.log(
|
|
11263
|
+
console.log(chalk15.dim(` \u2714 Installed missing ${componentId}.tsx`));
|
|
10988
11264
|
}
|
|
10989
11265
|
} catch {
|
|
10990
11266
|
}
|
|
@@ -10996,7 +11272,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
10996
11272
|
mkdirSync10(uiDir, { recursive: true });
|
|
10997
11273
|
const newContent = await generator.generate(def);
|
|
10998
11274
|
writeFileSync10(componentFile, newContent, "utf-8");
|
|
10999
|
-
console.log(
|
|
11275
|
+
console.log(chalk15.dim(` \u2714 Created missing ${componentId}.tsx`));
|
|
11000
11276
|
} catch {
|
|
11001
11277
|
}
|
|
11002
11278
|
}
|
|
@@ -11019,7 +11295,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
11019
11295
|
try {
|
|
11020
11296
|
const result = await provider.installComponent(componentId, projectRoot, { force: true });
|
|
11021
11297
|
if (result.success) {
|
|
11022
|
-
console.log(
|
|
11298
|
+
console.log(chalk15.dim(` \u2714 Reinstalled ${componentId}.tsx (added missing exports: ${missing.join(", ")})`));
|
|
11023
11299
|
}
|
|
11024
11300
|
} catch {
|
|
11025
11301
|
}
|
|
@@ -11029,7 +11305,7 @@ async function fixMissingComponentExports(projectRoot) {
|
|
|
11029
11305
|
try {
|
|
11030
11306
|
const newContent = await generator.generate(def);
|
|
11031
11307
|
writeFileSync10(componentFile, newContent, "utf-8");
|
|
11032
|
-
console.log(
|
|
11308
|
+
console.log(chalk15.dim(` \u2714 Regenerated ${componentId}.tsx (added missing exports: ${missing.join(", ")})`));
|
|
11033
11309
|
} catch {
|
|
11034
11310
|
}
|
|
11035
11311
|
}
|
|
@@ -11088,14 +11364,14 @@ async function healthCheck(port) {
|
|
|
11088
11364
|
try {
|
|
11089
11365
|
const res = await fetch(`http://localhost:${port}`);
|
|
11090
11366
|
if (res.status === 200) {
|
|
11091
|
-
console.log(
|
|
11367
|
+
console.log(chalk15.green(`
|
|
11092
11368
|
\u2705 Preview healthy at http://localhost:${port}`));
|
|
11093
11369
|
} else {
|
|
11094
|
-
console.log(
|
|
11370
|
+
console.log(chalk15.yellow(`
|
|
11095
11371
|
\u26A0 Preview returned ${res.status}. Run: coherent fix`));
|
|
11096
11372
|
}
|
|
11097
11373
|
} catch {
|
|
11098
|
-
console.log(
|
|
11374
|
+
console.log(chalk15.yellow(`
|
|
11099
11375
|
\u26A0 Preview not responding. Run: coherent fix`));
|
|
11100
11376
|
}
|
|
11101
11377
|
}
|
|
@@ -11134,29 +11410,29 @@ function launchWithMonitoring(projectRoot, restarts) {
|
|
|
11134
11410
|
const shadcnId = extractShadcnComponentFromModuleNotFound(msg);
|
|
11135
11411
|
if (shadcnId && !installingSet.has(shadcnId)) {
|
|
11136
11412
|
installingSet.add(shadcnId);
|
|
11137
|
-
console.log(
|
|
11413
|
+
console.log(chalk15.yellow(`
|
|
11138
11414
|
\u26A0 Missing component detected: ${shadcnId}`));
|
|
11139
|
-
console.log(
|
|
11415
|
+
console.log(chalk15.cyan(" Auto-installing..."));
|
|
11140
11416
|
autoInstallShadcnComponent(shadcnId, projectRoot).then((ok) => {
|
|
11141
11417
|
if (ok) {
|
|
11142
|
-
console.log(
|
|
11418
|
+
console.log(chalk15.green(` \u2714 Installed ${shadcnId}.tsx. Restarting...`));
|
|
11143
11419
|
intentionalRestart = true;
|
|
11144
11420
|
server.kill("SIGTERM");
|
|
11145
11421
|
launchWithMonitoring(projectRoot, restarts + 1).then(resolvePromise).catch(rejectPromise);
|
|
11146
11422
|
} else {
|
|
11147
|
-
console.log(
|
|
11423
|
+
console.log(chalk15.red(` \u2716 Could not install ${shadcnId}. Run: npx shadcn@latest add ${shadcnId}`));
|
|
11148
11424
|
}
|
|
11149
11425
|
});
|
|
11150
11426
|
} else if (!shadcnId) {
|
|
11151
11427
|
const pkg = extractPackageFromModuleNotFound(msg);
|
|
11152
11428
|
if (pkg && !installingSet.has(pkg)) {
|
|
11153
11429
|
installingSet.add(pkg);
|
|
11154
|
-
console.log(
|
|
11430
|
+
console.log(chalk15.yellow(`
|
|
11155
11431
|
\u26A0 Missing package detected: ${pkg}`));
|
|
11156
|
-
console.log(
|
|
11432
|
+
console.log(chalk15.cyan(" Auto-installing..."));
|
|
11157
11433
|
installPackages(projectRoot, [pkg]).then((ok) => {
|
|
11158
11434
|
if (ok) {
|
|
11159
|
-
console.log(
|
|
11435
|
+
console.log(chalk15.green(` \u2714 Installed ${pkg}. Restarting...`));
|
|
11160
11436
|
intentionalRestart = true;
|
|
11161
11437
|
server.kill("SIGTERM");
|
|
11162
11438
|
launchWithMonitoring(projectRoot, restarts + 1).then(resolvePromise).catch(rejectPromise);
|
|
@@ -11166,19 +11442,19 @@ function launchWithMonitoring(projectRoot, restarts) {
|
|
|
11166
11442
|
}
|
|
11167
11443
|
}
|
|
11168
11444
|
if (msg.includes("Failed to compile")) {
|
|
11169
|
-
console.log(
|
|
11170
|
-
console.log(
|
|
11171
|
-
console.log(
|
|
11445
|
+
console.log(chalk15.yellow("\n\u26A0 Compilation error detected."));
|
|
11446
|
+
console.log(chalk15.dim(' Hint: run "coherent fix" in another terminal to auto-fix'));
|
|
11447
|
+
console.log(chalk15.dim(' Or: coherent chat "fix the broken page"'));
|
|
11172
11448
|
}
|
|
11173
11449
|
});
|
|
11174
11450
|
server.on("exit", (code) => {
|
|
11175
11451
|
if (intentionalRestart) return;
|
|
11176
11452
|
if (code !== 0 && code !== null) {
|
|
11177
|
-
console.log(
|
|
11453
|
+
console.log(chalk15.red(`
|
|
11178
11454
|
\u274C Dev server exited with code ${code}`));
|
|
11179
|
-
console.log(
|
|
11455
|
+
console.log(chalk15.dim(' Check the output above. Fix and run "coherent preview" again.\n'));
|
|
11180
11456
|
} else {
|
|
11181
|
-
console.log(
|
|
11457
|
+
console.log(chalk15.dim("\n\u{1F44B} Dev server stopped"));
|
|
11182
11458
|
}
|
|
11183
11459
|
process.exit(code ?? 0);
|
|
11184
11460
|
});
|
|
@@ -11187,7 +11463,7 @@ function launchWithMonitoring(projectRoot, restarts) {
|
|
|
11187
11463
|
});
|
|
11188
11464
|
const shutdown = () => {
|
|
11189
11465
|
closeWatcher();
|
|
11190
|
-
console.log(
|
|
11466
|
+
console.log(chalk15.dim("\n\n\u{1F6D1} Stopping dev server..."));
|
|
11191
11467
|
server.kill("SIGTERM");
|
|
11192
11468
|
};
|
|
11193
11469
|
process.on("SIGINT", shutdown);
|
|
@@ -11199,9 +11475,9 @@ async function openBrowser(url) {
|
|
|
11199
11475
|
const open = await import("open");
|
|
11200
11476
|
await open.default(url);
|
|
11201
11477
|
} catch (error) {
|
|
11202
|
-
console.log(
|
|
11478
|
+
console.log(chalk15.yellow(`
|
|
11203
11479
|
\u26A0\uFE0F Could not open browser automatically`));
|
|
11204
|
-
console.log(
|
|
11480
|
+
console.log(chalk15.dim(` Please open ${url} manually`));
|
|
11205
11481
|
}
|
|
11206
11482
|
}
|
|
11207
11483
|
function startDevServer(projectRoot) {
|
|
@@ -11232,8 +11508,8 @@ async function previewCommand() {
|
|
|
11232
11508
|
try {
|
|
11233
11509
|
if (!checkProjectInitialized(projectRoot)) {
|
|
11234
11510
|
spinner.fail("Project not initialized");
|
|
11235
|
-
console.log(
|
|
11236
|
-
console.log(
|
|
11511
|
+
console.log(chalk15.red("\n\u274C Project not found"));
|
|
11512
|
+
console.log(chalk15.dim('Run "coherent init" first to create a project.'));
|
|
11237
11513
|
process.exit(1);
|
|
11238
11514
|
}
|
|
11239
11515
|
spinner.text = "Checking dependencies...";
|
|
@@ -11241,16 +11517,16 @@ async function previewCommand() {
|
|
|
11241
11517
|
spinner.warn("Dependencies not installed");
|
|
11242
11518
|
const pm = getPackageManager(projectRoot);
|
|
11243
11519
|
const installCommand = pm === "pnpm" ? "pnpm install" : "npm install";
|
|
11244
|
-
console.log(
|
|
11245
|
-
console.log(
|
|
11520
|
+
console.log(chalk15.yellow("\n\u26A0\uFE0F Dependencies not installed"));
|
|
11521
|
+
console.log(chalk15.cyan(`
|
|
11246
11522
|
Running ${installCommand}...
|
|
11247
11523
|
`));
|
|
11248
11524
|
const ok = await runInstall(projectRoot);
|
|
11249
11525
|
if (!ok) {
|
|
11250
|
-
console.error(
|
|
11526
|
+
console.error(chalk15.red('\n\u274C Install failed. Fix errors above and run "coherent preview" again.\n'));
|
|
11251
11527
|
process.exit(1);
|
|
11252
11528
|
}
|
|
11253
|
-
console.log(
|
|
11529
|
+
console.log(chalk15.green("\n\u2705 Dependencies installed\n"));
|
|
11254
11530
|
} else {
|
|
11255
11531
|
spinner.succeed("Project ready");
|
|
11256
11532
|
}
|
|
@@ -11273,27 +11549,27 @@ async function previewCommand() {
|
|
|
11273
11549
|
await fixMissingComponentExports(projectRoot);
|
|
11274
11550
|
await backfillPageAnalysis(projectRoot);
|
|
11275
11551
|
spinner.succeed("Project ready");
|
|
11276
|
-
console.log(
|
|
11277
|
-
console.log(
|
|
11552
|
+
console.log(chalk15.dim(" \u{1F4A1} Edited files manually? Run `coherent sync` to update the Design System.\n"));
|
|
11553
|
+
console.log(chalk15.blue("\n\u{1F680} Starting Next.js dev server...\n"));
|
|
11278
11554
|
await launchWithMonitoring(projectRoot, 0);
|
|
11279
11555
|
} catch (error) {
|
|
11280
11556
|
spinner.fail("Failed to start dev server");
|
|
11281
11557
|
if (error instanceof Error) {
|
|
11282
|
-
console.error(
|
|
11558
|
+
console.error(chalk15.red(`
|
|
11283
11559
|
\u274C ${error.message}`));
|
|
11284
11560
|
if (error.message.includes("package.json")) {
|
|
11285
|
-
console.log(
|
|
11286
|
-
console.log(
|
|
11561
|
+
console.log(chalk15.yellow("\n\u{1F4A1} Tip: Make sure you're in a Coherent project directory."));
|
|
11562
|
+
console.log(chalk15.dim(' Run "coherent init" to create a new project.'));
|
|
11287
11563
|
}
|
|
11288
11564
|
} else {
|
|
11289
|
-
console.error(
|
|
11565
|
+
console.error(chalk15.red("Unknown error occurred"));
|
|
11290
11566
|
}
|
|
11291
11567
|
process.exit(1);
|
|
11292
11568
|
}
|
|
11293
11569
|
}
|
|
11294
11570
|
|
|
11295
11571
|
// src/commands/export.ts
|
|
11296
|
-
import
|
|
11572
|
+
import chalk16 from "chalk";
|
|
11297
11573
|
import ora4 from "ora";
|
|
11298
11574
|
import { spawn as spawn2 } from "child_process";
|
|
11299
11575
|
import { existsSync as existsSync20, rmSync as rmSync4, readdirSync as readdirSync6 } from "fs";
|
|
@@ -11572,7 +11848,7 @@ async function exportCommand(options = {}) {
|
|
|
11572
11848
|
const missingDeps = await findMissingDepsInExport(outputDir);
|
|
11573
11849
|
if (missingDeps.length > 0) {
|
|
11574
11850
|
console.log(
|
|
11575
|
-
|
|
11851
|
+
chalk16.yellow(
|
|
11576
11852
|
"\n\u26A0\uFE0F Warning: exported code imports packages not in package.json: " + missingDeps.join(", ") + "\n Add them to dependencies and run npm install in the export dir.\n"
|
|
11577
11853
|
)
|
|
11578
11854
|
);
|
|
@@ -11588,7 +11864,7 @@ async function exportCommand(options = {}) {
|
|
|
11588
11864
|
spinner.succeed("Dependencies installed");
|
|
11589
11865
|
await ensureReadmeDeploySection(outputDir);
|
|
11590
11866
|
await patchNextConfigForExport(outputDir);
|
|
11591
|
-
console.log(
|
|
11867
|
+
console.log(chalk16.dim("\n Tip: run `coherent check` before export to catch quality issues.\n"));
|
|
11592
11868
|
let buildOk = false;
|
|
11593
11869
|
if (doBuild) {
|
|
11594
11870
|
spinner.start("Running next build...");
|
|
@@ -11598,7 +11874,7 @@ async function exportCommand(options = {}) {
|
|
|
11598
11874
|
spinner.succeed("Build: success");
|
|
11599
11875
|
} catch (e) {
|
|
11600
11876
|
spinner.fail("Build failed");
|
|
11601
|
-
if (e instanceof Error) console.error(
|
|
11877
|
+
if (e instanceof Error) console.error(chalk16.red(e.message));
|
|
11602
11878
|
}
|
|
11603
11879
|
} else {
|
|
11604
11880
|
buildOk = true;
|
|
@@ -11606,23 +11882,23 @@ async function exportCommand(options = {}) {
|
|
|
11606
11882
|
const pageCount = await countPages(outputDir);
|
|
11607
11883
|
const componentCount = countComponents(outputDir);
|
|
11608
11884
|
spinner.stop();
|
|
11609
|
-
console.log(
|
|
11610
|
-
console.log(
|
|
11611
|
-
console.log(
|
|
11612
|
-
console.log(
|
|
11885
|
+
console.log(chalk16.green("\n\u2705 Exported to " + outputDir + "\n"));
|
|
11886
|
+
console.log(chalk16.blue(" Pages: " + pageCount));
|
|
11887
|
+
console.log(chalk16.blue(" Components: " + componentCount + " (base + shared)"));
|
|
11888
|
+
console.log(chalk16.blue(" Build: " + (doBuild ? buildOk ? "success" : "failed" : "skipped (--no-build)")));
|
|
11613
11889
|
console.log("");
|
|
11614
|
-
console.log(
|
|
11615
|
-
console.log(
|
|
11890
|
+
console.log(chalk16.dim(" Deploy: npx vercel " + outputDir));
|
|
11891
|
+
console.log(chalk16.dim(" or: npx netlify deploy --dir " + outputDir + "/.next"));
|
|
11616
11892
|
console.log("");
|
|
11617
11893
|
} catch (error) {
|
|
11618
11894
|
spinner.fail("Export failed");
|
|
11619
|
-
if (error instanceof Error) console.error(
|
|
11895
|
+
if (error instanceof Error) console.error(chalk16.red("\n\u274C " + error.message));
|
|
11620
11896
|
process.exit(1);
|
|
11621
11897
|
}
|
|
11622
11898
|
}
|
|
11623
11899
|
|
|
11624
11900
|
// src/commands/status.ts
|
|
11625
|
-
import
|
|
11901
|
+
import chalk17 from "chalk";
|
|
11626
11902
|
import { basename } from "path";
|
|
11627
11903
|
import { DesignSystemManager as DesignSystemManager9 } from "@getcoherent/core";
|
|
11628
11904
|
function countTokens(tokens) {
|
|
@@ -11646,58 +11922,58 @@ async function statusCommand() {
|
|
|
11646
11922
|
try {
|
|
11647
11923
|
const project = findConfig();
|
|
11648
11924
|
if (!project) {
|
|
11649
|
-
console.log(
|
|
11925
|
+
console.log(chalk17.yellow("\u26A0\uFE0F Not in a Coherent project\n"));
|
|
11650
11926
|
console.log("Initialize a project:");
|
|
11651
|
-
console.log(
|
|
11927
|
+
console.log(chalk17.white(" $ coherent init\n"));
|
|
11652
11928
|
return;
|
|
11653
11929
|
}
|
|
11654
|
-
console.log(
|
|
11655
|
-
console.log(
|
|
11656
|
-
console.log(
|
|
11930
|
+
console.log(chalk17.cyan("\n\u2728 Current Project\n"));
|
|
11931
|
+
console.log(chalk17.gray("\u{1F4C1} Location: ") + chalk17.white(project.root));
|
|
11932
|
+
console.log(chalk17.gray("\u{1F4C4} Config: ") + chalk17.white(basename(project.configPath)));
|
|
11657
11933
|
console.log("");
|
|
11658
11934
|
try {
|
|
11659
11935
|
const manager = new DesignSystemManager9(project.configPath);
|
|
11660
11936
|
await manager.load();
|
|
11661
11937
|
const config2 = manager.getConfig();
|
|
11662
|
-
console.log(
|
|
11938
|
+
console.log(chalk17.cyan("\u{1F4CA} Statistics:\n"));
|
|
11663
11939
|
const pageCount = Array.isArray(config2.pages) ? config2.pages.length : Object.keys(config2.pages || {}).length;
|
|
11664
|
-
console.log(
|
|
11940
|
+
console.log(chalk17.gray(" Pages: ") + chalk17.white(String(pageCount)));
|
|
11665
11941
|
const componentCount = Array.isArray(config2.components) ? config2.components.length : Object.keys(config2.components || {}).length;
|
|
11666
|
-
console.log(
|
|
11942
|
+
console.log(chalk17.gray(" Components: ") + chalk17.white(String(componentCount)));
|
|
11667
11943
|
const tokenCount = countTokens(config2.tokens);
|
|
11668
|
-
console.log(
|
|
11944
|
+
console.log(chalk17.gray(" Design tokens: ") + chalk17.white(String(tokenCount)));
|
|
11669
11945
|
console.log("");
|
|
11670
11946
|
const recent = readRecentChanges(project.root);
|
|
11671
11947
|
if (recent.length > 0) {
|
|
11672
|
-
console.log(
|
|
11948
|
+
console.log(chalk17.cyan("\u{1F4DD} Recent changes:\n"));
|
|
11673
11949
|
recent.slice(0, 5).forEach((change) => {
|
|
11674
11950
|
const ago = formatTimeAgo(change.timestamp);
|
|
11675
|
-
console.log(
|
|
11951
|
+
console.log(chalk17.gray(" \u2022 ") + chalk17.white(change.description) + chalk17.gray(` (${ago})`));
|
|
11676
11952
|
});
|
|
11677
11953
|
console.log("");
|
|
11678
11954
|
}
|
|
11679
|
-
console.log(
|
|
11680
|
-
console.log(
|
|
11681
|
-
console.log(
|
|
11682
|
-
console.log(
|
|
11955
|
+
console.log(chalk17.cyan("\u{1F680} Quick actions:\n"));
|
|
11956
|
+
console.log(chalk17.white(' $ coherent chat "add new page"'));
|
|
11957
|
+
console.log(chalk17.white(" $ coherent preview"));
|
|
11958
|
+
console.log(chalk17.white(" $ coherent export"));
|
|
11683
11959
|
console.log("");
|
|
11684
11960
|
} catch (error) {
|
|
11685
|
-
console.error(
|
|
11961
|
+
console.error(chalk17.red("Error loading config:"));
|
|
11686
11962
|
if (error instanceof Error) {
|
|
11687
|
-
console.error(
|
|
11963
|
+
console.error(chalk17.red(` ${error.message}`));
|
|
11688
11964
|
} else {
|
|
11689
|
-
console.error(
|
|
11965
|
+
console.error(chalk17.red(" Unknown error"));
|
|
11690
11966
|
}
|
|
11691
11967
|
console.log("");
|
|
11692
11968
|
}
|
|
11693
11969
|
} catch (error) {
|
|
11694
|
-
console.error(
|
|
11970
|
+
console.error(chalk17.red("\u274C Command failed:"), error instanceof Error ? error.message : "Unknown error");
|
|
11695
11971
|
process.exit(1);
|
|
11696
11972
|
}
|
|
11697
11973
|
}
|
|
11698
11974
|
|
|
11699
11975
|
// src/commands/regenerate-docs.ts
|
|
11700
|
-
import
|
|
11976
|
+
import chalk18 from "chalk";
|
|
11701
11977
|
import ora5 from "ora";
|
|
11702
11978
|
import { DesignSystemManager as DesignSystemManager10 } from "@getcoherent/core";
|
|
11703
11979
|
import { ProjectScaffolder as ProjectScaffolder2 } from "@getcoherent/core";
|
|
@@ -11705,9 +11981,9 @@ async function regenerateDocsCommand() {
|
|
|
11705
11981
|
try {
|
|
11706
11982
|
const project = findConfig();
|
|
11707
11983
|
if (!project) {
|
|
11708
|
-
console.log(
|
|
11984
|
+
console.log(chalk18.yellow("\u26A0\uFE0F Not in a Coherent project\n"));
|
|
11709
11985
|
console.log("Run this command from a project root that has design-system.config.ts");
|
|
11710
|
-
console.log(
|
|
11986
|
+
console.log(chalk18.white(" $ coherent init # in an empty folder first\n"));
|
|
11711
11987
|
process.exit(1);
|
|
11712
11988
|
}
|
|
11713
11989
|
const spinner = ora5("Regenerating documentation pages...").start();
|
|
@@ -11719,23 +11995,23 @@ async function regenerateDocsCommand() {
|
|
|
11719
11995
|
await scaffolder.generateDocsPages();
|
|
11720
11996
|
spinner.succeed("Documentation pages updated");
|
|
11721
11997
|
console.log(
|
|
11722
|
-
|
|
11998
|
+
chalk18.gray(
|
|
11723
11999
|
"\nUpdated: app/design-system/docs/ (layout, page, components, tokens, for-designers, recommendations)\n"
|
|
11724
12000
|
)
|
|
11725
12001
|
);
|
|
11726
12002
|
} catch (err) {
|
|
11727
12003
|
spinner.fail("Failed to regenerate docs");
|
|
11728
|
-
console.error(
|
|
12004
|
+
console.error(chalk18.red(err instanceof Error ? err.message : String(err)));
|
|
11729
12005
|
process.exit(1);
|
|
11730
12006
|
}
|
|
11731
12007
|
} catch (error) {
|
|
11732
|
-
console.error(
|
|
12008
|
+
console.error(chalk18.red("\u274C Command failed:"), error instanceof Error ? error.message : "Unknown error");
|
|
11733
12009
|
process.exit(1);
|
|
11734
12010
|
}
|
|
11735
12011
|
}
|
|
11736
12012
|
|
|
11737
12013
|
// src/commands/fix.ts
|
|
11738
|
-
import
|
|
12014
|
+
import chalk19 from "chalk";
|
|
11739
12015
|
import { readdirSync as readdirSync7, readFileSync as readFileSync15, existsSync as existsSync21, writeFileSync as writeFileSync11, rmSync as rmSync5, mkdirSync as mkdirSync7 } from "fs";
|
|
11740
12016
|
import { resolve as resolve12, join as join16 } from "path";
|
|
11741
12017
|
import {
|
|
@@ -11787,31 +12063,31 @@ async function fixCommand(opts = {}) {
|
|
|
11787
12063
|
const fixes = [];
|
|
11788
12064
|
const remaining = [];
|
|
11789
12065
|
if (dryRun) {
|
|
11790
|
-
console.log(
|
|
12066
|
+
console.log(chalk19.cyan("\ncoherent fix --dry-run\n"));
|
|
11791
12067
|
} else {
|
|
11792
|
-
console.log(
|
|
12068
|
+
console.log(chalk19.cyan("\ncoherent fix\n"));
|
|
11793
12069
|
}
|
|
11794
12070
|
if (!skipCache) {
|
|
11795
12071
|
const nextDir = join16(projectRoot, ".next");
|
|
11796
12072
|
if (existsSync21(nextDir)) {
|
|
11797
12073
|
if (!dryRun) rmSync5(nextDir, { recursive: true, force: true });
|
|
11798
12074
|
fixes.push("Cleared build cache");
|
|
11799
|
-
console.log(
|
|
12075
|
+
console.log(chalk19.green(" \u2714 Cleared build cache"));
|
|
11800
12076
|
}
|
|
11801
12077
|
}
|
|
11802
12078
|
const missingPkgs = await findMissingPackages(projectRoot);
|
|
11803
12079
|
if (missingPkgs.length > 0) {
|
|
11804
12080
|
if (dryRun) {
|
|
11805
12081
|
fixes.push(`Would install packages: ${missingPkgs.join(", ")}`);
|
|
11806
|
-
console.log(
|
|
12082
|
+
console.log(chalk19.green(` \u2714 Would install packages: ${missingPkgs.join(", ")}`));
|
|
11807
12083
|
} else {
|
|
11808
12084
|
const ok = await installPackages(projectRoot, missingPkgs);
|
|
11809
12085
|
if (ok) {
|
|
11810
12086
|
fixes.push(`Installed missing packages: ${missingPkgs.join(", ")}`);
|
|
11811
|
-
console.log(
|
|
12087
|
+
console.log(chalk19.green(` \u2714 Installed missing packages: ${missingPkgs.join(", ")}`));
|
|
11812
12088
|
} else {
|
|
11813
12089
|
remaining.push(`Failed to install: ${missingPkgs.join(", ")}. Run: npm install ${missingPkgs.join(" ")}`);
|
|
11814
|
-
console.log(
|
|
12090
|
+
console.log(chalk19.yellow(` \u26A0 Could not install: ${missingPkgs.join(", ")}`));
|
|
11815
12091
|
}
|
|
11816
12092
|
}
|
|
11817
12093
|
}
|
|
@@ -11848,7 +12124,7 @@ async function fixCommand(opts = {}) {
|
|
|
11848
12124
|
if (toInstall.length > 0) {
|
|
11849
12125
|
if (dryRun) {
|
|
11850
12126
|
fixes.push(`Would install components: ${toInstall.join(", ")}`);
|
|
11851
|
-
console.log(
|
|
12127
|
+
console.log(chalk19.green(` \u2714 Would install components: ${toInstall.join(", ")}`));
|
|
11852
12128
|
} else {
|
|
11853
12129
|
let installed = 0;
|
|
11854
12130
|
for (const componentId of toInstall) {
|
|
@@ -11877,14 +12153,14 @@ async function fixCommand(opts = {}) {
|
|
|
11877
12153
|
installed++;
|
|
11878
12154
|
} catch (err) {
|
|
11879
12155
|
console.log(
|
|
11880
|
-
|
|
12156
|
+
chalk19.yellow(` \u26A0 Failed to install ${componentId}: ${err instanceof Error ? err.message : "unknown"}`)
|
|
11881
12157
|
);
|
|
11882
12158
|
}
|
|
11883
12159
|
}
|
|
11884
12160
|
if (installed > 0) {
|
|
11885
12161
|
await dsm.save();
|
|
11886
12162
|
fixes.push(`Installed missing components: ${toInstall.join(", ")}`);
|
|
11887
|
-
console.log(
|
|
12163
|
+
console.log(chalk19.green(` \u2714 Installed missing components: ${toInstall.join(", ")}`));
|
|
11888
12164
|
}
|
|
11889
12165
|
}
|
|
11890
12166
|
}
|
|
@@ -11904,7 +12180,7 @@ async function fixCommand(opts = {}) {
|
|
|
11904
12180
|
if (syntaxFixed > 0) {
|
|
11905
12181
|
const verb = dryRun ? "Would fix" : "Fixed";
|
|
11906
12182
|
fixes.push(`${verb} syntax in ${syntaxFixed} file(s)`);
|
|
11907
|
-
console.log(
|
|
12183
|
+
console.log(chalk19.green(` \u2714 ${verb} syntax: ${syntaxFixed} file(s) (use client, metadata, quotes)`));
|
|
11908
12184
|
}
|
|
11909
12185
|
if (!skipQuality) {
|
|
11910
12186
|
let qualityFixCount = 0;
|
|
@@ -11922,7 +12198,7 @@ async function fixCommand(opts = {}) {
|
|
|
11922
12198
|
const uniqueFixes = [...new Set(qualityFixDetails)];
|
|
11923
12199
|
const verb = dryRun ? "Would fix" : "Fixed";
|
|
11924
12200
|
fixes.push(`${verb} quality in ${qualityFixCount} file(s)`);
|
|
11925
|
-
console.log(
|
|
12201
|
+
console.log(chalk19.green(` \u2714 ${verb} ${uniqueFixes.length} quality issue type(s): ${uniqueFixes.join(", ")}`));
|
|
11926
12202
|
}
|
|
11927
12203
|
}
|
|
11928
12204
|
let totalErrors = 0;
|
|
@@ -11967,13 +12243,13 @@ async function fixCommand(opts = {}) {
|
|
|
11967
12243
|
if (dryRun) {
|
|
11968
12244
|
fixes.push(`Would update ${o.id} path to ${newPath}`);
|
|
11969
12245
|
} else {
|
|
11970
|
-
console.log(
|
|
12246
|
+
console.log(chalk19.green(` \u2714 Updated ${o.id} (${o.name}) path \u2192 ${newPath}`));
|
|
11971
12247
|
}
|
|
11972
12248
|
} else {
|
|
11973
12249
|
if (dryRun) {
|
|
11974
12250
|
fixes.push(`Would remove orphaned ${o.id} (${o.name})`);
|
|
11975
12251
|
} else {
|
|
11976
|
-
console.log(
|
|
12252
|
+
console.log(chalk19.green(` \u2714 Removed orphaned ${o.id} (${o.name}) \u2014 file missing`));
|
|
11977
12253
|
}
|
|
11978
12254
|
}
|
|
11979
12255
|
}
|
|
@@ -11986,7 +12262,7 @@ async function fixCommand(opts = {}) {
|
|
|
11986
12262
|
entry.usedIn = fullActual;
|
|
11987
12263
|
manifestModified = true;
|
|
11988
12264
|
if (!dryRun) {
|
|
11989
|
-
console.log(
|
|
12265
|
+
console.log(chalk19.green(` \u2714 Updated ${entry.id} usedIn: ${fullActual.join(", ") || "none"}`));
|
|
11990
12266
|
}
|
|
11991
12267
|
}
|
|
11992
12268
|
}
|
|
@@ -12004,7 +12280,7 @@ async function fixCommand(opts = {}) {
|
|
|
12004
12280
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12005
12281
|
});
|
|
12006
12282
|
manifest.nextId++;
|
|
12007
|
-
console.log(
|
|
12283
|
+
console.log(chalk19.green(` \u2714 Registered ${id} (${comp.name}) from ${comp.file}`));
|
|
12008
12284
|
} else {
|
|
12009
12285
|
fixes.push(`Would register ${comp.name} from ${comp.file}`);
|
|
12010
12286
|
}
|
|
@@ -12024,33 +12300,33 @@ async function fixCommand(opts = {}) {
|
|
|
12024
12300
|
} catch {
|
|
12025
12301
|
}
|
|
12026
12302
|
if (fixes.length === 0 && totalErrors === 0 && totalWarnings === 0 && remaining.length === 0) {
|
|
12027
|
-
console.log(
|
|
12028
|
-
console.log(
|
|
12303
|
+
console.log(chalk19.green("\n \u2705 Everything looks good \u2014 no issues found\n"));
|
|
12304
|
+
console.log(chalk19.cyan(" Run: coherent preview\n"));
|
|
12029
12305
|
return;
|
|
12030
12306
|
}
|
|
12031
12307
|
if (fixes.length > 0) console.log("");
|
|
12032
12308
|
if (totalErrors > 0 || totalWarnings > 0 || remaining.length > 0) {
|
|
12033
|
-
console.log(
|
|
12034
|
-
console.log(
|
|
12309
|
+
console.log(chalk19.dim(" \u2500".repeat(25)));
|
|
12310
|
+
console.log(chalk19.yellow(`
|
|
12035
12311
|
Remaining (need manual fix or AI):`));
|
|
12036
12312
|
for (const { path: path4, report } of fileIssues) {
|
|
12037
|
-
console.log(
|
|
12313
|
+
console.log(chalk19.dim(` \u{1F4C4} ${path4}`));
|
|
12038
12314
|
console.log(report);
|
|
12039
12315
|
}
|
|
12040
12316
|
for (const r of remaining) {
|
|
12041
|
-
console.log(
|
|
12317
|
+
console.log(chalk19.yellow(` \u26A0 ${r}`));
|
|
12042
12318
|
}
|
|
12043
12319
|
console.log("");
|
|
12044
12320
|
const parts = [];
|
|
12045
|
-
if (totalErrors > 0) parts.push(
|
|
12046
|
-
if (totalWarnings > 0) parts.push(
|
|
12321
|
+
if (totalErrors > 0) parts.push(chalk19.red(`\u274C ${totalErrors} error(s)`));
|
|
12322
|
+
if (totalWarnings > 0) parts.push(chalk19.yellow(`\u26A0 ${totalWarnings} warning(s)`));
|
|
12047
12323
|
if (parts.length > 0) console.log(` ${parts.join(" ")}`);
|
|
12048
12324
|
}
|
|
12049
|
-
console.log(
|
|
12325
|
+
console.log(chalk19.cyan("\n Run: coherent preview\n"));
|
|
12050
12326
|
}
|
|
12051
12327
|
|
|
12052
12328
|
// src/commands/check.ts
|
|
12053
|
-
import
|
|
12329
|
+
import chalk20 from "chalk";
|
|
12054
12330
|
import { resolve as resolve13 } from "path";
|
|
12055
12331
|
import { readdirSync as readdirSync8, readFileSync as readFileSync16, statSync as statSync2, existsSync as existsSync22 } from "fs";
|
|
12056
12332
|
import { loadManifest as loadManifest11 } from "@getcoherent/core";
|
|
@@ -12098,7 +12374,7 @@ async function checkCommand(opts = {}) {
|
|
|
12098
12374
|
const appDir = resolve13(projectRoot, "app");
|
|
12099
12375
|
const files = findTsxFiles(appDir);
|
|
12100
12376
|
result.pages.total = files.length;
|
|
12101
|
-
if (!opts.json) console.log(
|
|
12377
|
+
if (!opts.json) console.log(chalk20.cyan("\n \u{1F4C4} Pages") + chalk20.dim(` (${files.length} scanned)
|
|
12102
12378
|
`));
|
|
12103
12379
|
const autoFixableTypes = /* @__PURE__ */ new Set([
|
|
12104
12380
|
"RAW_COLOR",
|
|
@@ -12128,7 +12404,7 @@ async function checkCommand(opts = {}) {
|
|
|
12128
12404
|
result.autoFixable += fileAutoFixable;
|
|
12129
12405
|
if (filteredIssues.length === 0) {
|
|
12130
12406
|
result.pages.clean++;
|
|
12131
|
-
if (!opts.json) console.log(
|
|
12407
|
+
if (!opts.json) console.log(chalk20.green(` \u2714 ${relativePath}`) + chalk20.dim(" \u2014 clean"));
|
|
12132
12408
|
continue;
|
|
12133
12409
|
}
|
|
12134
12410
|
if (errors > 0) result.pages.withErrors++;
|
|
@@ -12141,9 +12417,9 @@ async function checkCommand(opts = {}) {
|
|
|
12141
12417
|
});
|
|
12142
12418
|
if (!opts.json) {
|
|
12143
12419
|
const parts = [];
|
|
12144
|
-
if (errors > 0) parts.push(
|
|
12145
|
-
if (warnings > 0) parts.push(
|
|
12146
|
-
console.log(
|
|
12420
|
+
if (errors > 0) parts.push(chalk20.red(`${errors} error(s)`));
|
|
12421
|
+
if (warnings > 0) parts.push(chalk20.yellow(`${warnings} warning(s)`));
|
|
12422
|
+
console.log(chalk20.yellow(` \u26A0 ${relativePath}`) + chalk20.dim(` \u2014 ${parts.join(", ")}`));
|
|
12147
12423
|
console.log(formatIssues(filteredIssues));
|
|
12148
12424
|
}
|
|
12149
12425
|
}
|
|
@@ -12169,15 +12445,15 @@ async function checkCommand(opts = {}) {
|
|
|
12169
12445
|
}
|
|
12170
12446
|
}
|
|
12171
12447
|
if (!opts.json && result.links.broken.length > 0) {
|
|
12172
|
-
console.log(
|
|
12173
|
-
\u{1F517} Internal Links`) +
|
|
12448
|
+
console.log(chalk20.yellow(`
|
|
12449
|
+
\u{1F517} Internal Links`) + chalk20.dim(` (${result.links.total} scanned)
|
|
12174
12450
|
`));
|
|
12175
12451
|
for (const b of result.links.broken) {
|
|
12176
|
-
console.log(
|
|
12452
|
+
console.log(chalk20.red(` \u2717 ${b.file}:${b.line}`) + chalk20.dim(` \u2192 ${b.href} (route does not exist)`));
|
|
12177
12453
|
}
|
|
12178
12454
|
} else if (!opts.json && result.links.total > 0) {
|
|
12179
|
-
console.log(
|
|
12180
|
-
\u{1F517} Internal Links`) +
|
|
12455
|
+
console.log(chalk20.green(`
|
|
12456
|
+
\u{1F517} Internal Links`) + chalk20.dim(` \u2014 all ${result.links.total} links resolve \u2713`));
|
|
12181
12457
|
}
|
|
12182
12458
|
try {
|
|
12183
12459
|
const manifest = await loadManifest11(project.root);
|
|
@@ -12186,7 +12462,7 @@ async function checkCommand(opts = {}) {
|
|
|
12186
12462
|
const fullPath = resolve13(project.root, entry.file);
|
|
12187
12463
|
if (!existsSync22(fullPath)) {
|
|
12188
12464
|
result.pages.withErrors++;
|
|
12189
|
-
if (!opts.json) console.log(
|
|
12465
|
+
if (!opts.json) console.log(chalk20.red(`
|
|
12190
12466
|
\u2717 Missing shared component file: ${entry.id} (${entry.file})`));
|
|
12191
12467
|
}
|
|
12192
12468
|
}
|
|
@@ -12198,8 +12474,8 @@ async function checkCommand(opts = {}) {
|
|
|
12198
12474
|
try {
|
|
12199
12475
|
const manifest = await loadManifest11(projectRoot);
|
|
12200
12476
|
if (!opts.json && manifest.shared.length > 0) {
|
|
12201
|
-
console.log(
|
|
12202
|
-
\u{1F9E9} Shared Components`) +
|
|
12477
|
+
console.log(chalk20.cyan(`
|
|
12478
|
+
\u{1F9E9} Shared Components`) + chalk20.dim(` (${manifest.shared.length} registered)
|
|
12203
12479
|
`));
|
|
12204
12480
|
}
|
|
12205
12481
|
let consistent = 0;
|
|
@@ -12213,8 +12489,8 @@ async function checkCommand(opts = {}) {
|
|
|
12213
12489
|
if (!fileExists) {
|
|
12214
12490
|
_orphaned++;
|
|
12215
12491
|
if (!opts.json) {
|
|
12216
|
-
console.log(
|
|
12217
|
-
console.log(
|
|
12492
|
+
console.log(chalk20.red(` \u274C ${entry.id} (${entry.name}) \u2014 file missing: ${entry.file}`));
|
|
12493
|
+
console.log(chalk20.dim(` Fix: coherent fix or coherent sync`));
|
|
12218
12494
|
}
|
|
12219
12495
|
continue;
|
|
12220
12496
|
}
|
|
@@ -12225,11 +12501,11 @@ async function checkCommand(opts = {}) {
|
|
|
12225
12501
|
_nameMismatch++;
|
|
12226
12502
|
if (!opts.json) {
|
|
12227
12503
|
console.log(
|
|
12228
|
-
|
|
12504
|
+
chalk20.yellow(
|
|
12229
12505
|
` \u26A0 ${entry.id} \u2014 manifest name "${entry.name}" doesn't match export "${actualExports[0]}"`
|
|
12230
12506
|
)
|
|
12231
12507
|
);
|
|
12232
|
-
console.log(
|
|
12508
|
+
console.log(chalk20.dim(` Fix: coherent sync`));
|
|
12233
12509
|
}
|
|
12234
12510
|
}
|
|
12235
12511
|
} catch {
|
|
@@ -12244,35 +12520,35 @@ async function checkCommand(opts = {}) {
|
|
|
12244
12520
|
if (totalUsage === 0) {
|
|
12245
12521
|
unused++;
|
|
12246
12522
|
if (!opts.json) {
|
|
12247
|
-
console.log(
|
|
12248
|
-
console.log(
|
|
12523
|
+
console.log(chalk20.blue(` \u2139 ${entry.id} (${entry.name}) \u2014 registered but not used anywhere`));
|
|
12524
|
+
console.log(chalk20.dim(` Remove: coherent components shared remove ${entry.id}`));
|
|
12249
12525
|
}
|
|
12250
12526
|
} else {
|
|
12251
12527
|
consistent++;
|
|
12252
12528
|
const usageDesc = inLayout ? `layout + ${actualUsedIn.length} page(s)` : `${actualUsedIn.length} page(s)`;
|
|
12253
12529
|
if (!opts.json) {
|
|
12254
|
-
const staleNote = isStale ?
|
|
12255
|
-
console.log(
|
|
12530
|
+
const staleNote = isStale ? chalk20.yellow(" [usedIn stale]") : "";
|
|
12531
|
+
console.log(chalk20.green(` \u2714 ${entry.id} (${entry.name})`) + chalk20.dim(` \u2014 ${usageDesc}`) + staleNote);
|
|
12256
12532
|
}
|
|
12257
12533
|
}
|
|
12258
12534
|
}
|
|
12259
12535
|
const unregistered = findUnregisteredComponents(projectRoot, manifest);
|
|
12260
12536
|
if (unregistered.length > 0 && !opts.json) {
|
|
12261
|
-
console.log(
|
|
12537
|
+
console.log(chalk20.cyan(`
|
|
12262
12538
|
\u{1F4E6} Unregistered components found:`));
|
|
12263
12539
|
for (const comp of unregistered) {
|
|
12264
|
-
console.log(
|
|
12265
|
-
console.log(
|
|
12540
|
+
console.log(chalk20.blue(` \u2139 ${comp.name}`) + chalk20.dim(` \u2014 ${comp.file} (not in manifest)`));
|
|
12541
|
+
console.log(chalk20.dim(` Register: coherent sync`));
|
|
12266
12542
|
}
|
|
12267
12543
|
}
|
|
12268
12544
|
const inlineDupes = findInlineDuplicates(projectRoot, manifest);
|
|
12269
12545
|
if (inlineDupes.length > 0 && !opts.json) {
|
|
12270
|
-
console.log(
|
|
12546
|
+
console.log(chalk20.cyan(`
|
|
12271
12547
|
\u{1F50D} Inline duplicates:`));
|
|
12272
12548
|
for (const dup of inlineDupes) {
|
|
12273
|
-
console.log(
|
|
12549
|
+
console.log(chalk20.yellow(` \u26A0 ${dup.pageFile}`) + chalk20.dim(` has inline ${dup.componentName}`));
|
|
12274
12550
|
console.log(
|
|
12275
|
-
|
|
12551
|
+
chalk20.dim(
|
|
12276
12552
|
` Use shared: import { ${dup.componentName} } from "@/${dup.sharedFile.replace(".tsx", "")}"`
|
|
12277
12553
|
)
|
|
12278
12554
|
);
|
|
@@ -12299,24 +12575,24 @@ async function checkCommand(opts = {}) {
|
|
|
12299
12575
|
console.log(JSON.stringify(result, null, 2));
|
|
12300
12576
|
return;
|
|
12301
12577
|
}
|
|
12302
|
-
console.log(
|
|
12578
|
+
console.log(chalk20.dim("\n " + "\u2500".repeat(50)));
|
|
12303
12579
|
const summaryParts = [];
|
|
12304
12580
|
if (!skipPages) {
|
|
12305
|
-
summaryParts.push(`${
|
|
12306
|
-
if (result.pages.withErrors > 0) summaryParts.push(
|
|
12307
|
-
if (result.pages.withWarnings > 0) summaryParts.push(
|
|
12581
|
+
summaryParts.push(`${chalk20.green(`${result.pages.clean} clean`)} pages`);
|
|
12582
|
+
if (result.pages.withErrors > 0) summaryParts.push(chalk20.red(`${result.pages.withErrors} with errors`));
|
|
12583
|
+
if (result.pages.withWarnings > 0) summaryParts.push(chalk20.yellow(`${result.pages.withWarnings} with warnings`));
|
|
12308
12584
|
}
|
|
12309
12585
|
if (!skipShared && result.shared.total > 0) {
|
|
12310
12586
|
summaryParts.push(`${result.shared.consistent} healthy shared`);
|
|
12311
12587
|
if (result.shared.unused > 0) summaryParts.push(`${result.shared.unused} unused`);
|
|
12312
12588
|
}
|
|
12313
12589
|
if (result.links.broken.length > 0) {
|
|
12314
|
-
summaryParts.push(
|
|
12590
|
+
summaryParts.push(chalk20.red(`${result.links.broken.length} broken link(s)`));
|
|
12315
12591
|
}
|
|
12316
12592
|
console.log(`
|
|
12317
12593
|
${summaryParts.join(" | ")}`);
|
|
12318
12594
|
if (result.autoFixable > 0) {
|
|
12319
|
-
console.log(
|
|
12595
|
+
console.log(chalk20.cyan(`
|
|
12320
12596
|
Auto-fixable: ${result.autoFixable} issues. Run: coherent fix`));
|
|
12321
12597
|
}
|
|
12322
12598
|
console.log("");
|
|
@@ -12325,21 +12601,21 @@ async function checkCommand(opts = {}) {
|
|
|
12325
12601
|
}
|
|
12326
12602
|
|
|
12327
12603
|
// src/commands/repair.ts
|
|
12328
|
-
import
|
|
12604
|
+
import chalk21 from "chalk";
|
|
12329
12605
|
async function repairCommand() {
|
|
12330
|
-
console.log(
|
|
12606
|
+
console.log(chalk21.dim(" \u2139\uFE0F `coherent repair` is deprecated \u2014 use `coherent fix` instead\n"));
|
|
12331
12607
|
await fixCommand();
|
|
12332
12608
|
}
|
|
12333
12609
|
|
|
12334
12610
|
// src/commands/doctor.ts
|
|
12335
|
-
import
|
|
12611
|
+
import chalk22 from "chalk";
|
|
12336
12612
|
async function doctorCommand() {
|
|
12337
|
-
console.log(
|
|
12613
|
+
console.log(chalk22.dim(" \u2139\uFE0F `coherent doctor` is deprecated \u2014 use `coherent fix` instead\n"));
|
|
12338
12614
|
await fixCommand();
|
|
12339
12615
|
}
|
|
12340
12616
|
|
|
12341
12617
|
// src/commands/rules.ts
|
|
12342
|
-
import
|
|
12618
|
+
import chalk23 from "chalk";
|
|
12343
12619
|
async function rulesCommand() {
|
|
12344
12620
|
try {
|
|
12345
12621
|
const result = await regenerateCursorRules();
|
|
@@ -12350,31 +12626,31 @@ async function rulesCommand() {
|
|
|
12350
12626
|
if (result.sharedCount !== void 0) parts.push(`${result.sharedCount} shared components`);
|
|
12351
12627
|
if (result.tokenKeys !== void 0) parts.push(`${result.tokenKeys} design token keys`);
|
|
12352
12628
|
const summary = parts.length > 0 ? ` (${parts.join(", ")})` : "";
|
|
12353
|
-
console.log(
|
|
12629
|
+
console.log(chalk23.green(`\u2714 Updated .cursorrules and CLAUDE.md${summary}
|
|
12354
12630
|
`));
|
|
12355
12631
|
} catch (error) {
|
|
12356
|
-
console.error(
|
|
12632
|
+
console.error(chalk23.red("\u274C Command failed:"), error instanceof Error ? error.message : "Unknown error");
|
|
12357
12633
|
process.exit(1);
|
|
12358
12634
|
}
|
|
12359
12635
|
}
|
|
12360
12636
|
|
|
12361
12637
|
// src/commands/validate.ts
|
|
12362
|
-
import
|
|
12638
|
+
import chalk24 from "chalk";
|
|
12363
12639
|
async function validateCommand() {
|
|
12364
|
-
console.log(
|
|
12640
|
+
console.log(chalk24.dim(" \u2139\uFE0F `coherent validate` is deprecated \u2014 use `coherent check` instead\n"));
|
|
12365
12641
|
await checkCommand({ pages: true });
|
|
12366
12642
|
}
|
|
12367
12643
|
|
|
12368
12644
|
// src/commands/audit.ts
|
|
12369
|
-
import
|
|
12645
|
+
import chalk25 from "chalk";
|
|
12370
12646
|
async function auditCommand(options) {
|
|
12371
|
-
console.log(
|
|
12647
|
+
console.log(chalk25.dim(" \u2139\uFE0F `coherent audit` is deprecated \u2014 use `coherent check` instead\n"));
|
|
12372
12648
|
await checkCommand({ shared: true, json: options.json });
|
|
12373
12649
|
}
|
|
12374
12650
|
|
|
12375
12651
|
// src/commands/components.ts
|
|
12376
12652
|
import { Command } from "commander";
|
|
12377
|
-
import
|
|
12653
|
+
import chalk26 from "chalk";
|
|
12378
12654
|
import {
|
|
12379
12655
|
DesignSystemManager as DesignSystemManager12,
|
|
12380
12656
|
ComponentManager as ComponentManager7,
|
|
@@ -12425,9 +12701,9 @@ function createComponentsCommand() {
|
|
|
12425
12701
|
console.log(JSON.stringify({ shared: manifest.shared, ui: installed2 }, null, 2));
|
|
12426
12702
|
return;
|
|
12427
12703
|
}
|
|
12428
|
-
console.log(
|
|
12704
|
+
console.log(chalk26.bold("\n\u{1F4E6} Shared Components"));
|
|
12429
12705
|
if (manifest.shared.length === 0) {
|
|
12430
|
-
console.log(
|
|
12706
|
+
console.log(chalk26.gray(" None yet. Generate pages with header/footer to create them.\n"));
|
|
12431
12707
|
} else {
|
|
12432
12708
|
const order = { layout: 0, section: 1, widget: 2 };
|
|
12433
12709
|
const sorted = [...manifest.shared].sort(
|
|
@@ -12435,9 +12711,9 @@ function createComponentsCommand() {
|
|
|
12435
12711
|
);
|
|
12436
12712
|
console.log("");
|
|
12437
12713
|
sorted.forEach((entry) => {
|
|
12438
|
-
const usage = entry.usedIn.length === 0 ?
|
|
12714
|
+
const usage = entry.usedIn.length === 0 ? chalk26.gray("unused") : entry.usedIn.includes("app/layout.tsx") ? chalk26.green("all pages") : chalk26.gray(entry.usedIn.join(", "));
|
|
12439
12715
|
console.log(
|
|
12440
|
-
` ${
|
|
12716
|
+
` ${chalk26.cyan(entry.id.padEnd(8))} ${chalk26.white(entry.name.padEnd(18))} ${chalk26.gray(entry.type.padEnd(9))} ${usage}`
|
|
12441
12717
|
);
|
|
12442
12718
|
});
|
|
12443
12719
|
console.log("");
|
|
@@ -12446,24 +12722,24 @@ function createComponentsCommand() {
|
|
|
12446
12722
|
const availableShadcn = listShadcnComponents();
|
|
12447
12723
|
const installedIds = new Set(installed.map((c) => c.id));
|
|
12448
12724
|
const notInstalled = availableShadcn.filter((id) => !installedIds.has(id));
|
|
12449
|
-
console.log(
|
|
12725
|
+
console.log(chalk26.bold("\u{1F9E9} UI Components (shadcn)"));
|
|
12450
12726
|
if (installed.length === 0) {
|
|
12451
|
-
console.log(
|
|
12727
|
+
console.log(chalk26.gray(" None installed yet.\n"));
|
|
12452
12728
|
} else {
|
|
12453
12729
|
const names = installed.map((c) => c.name).sort();
|
|
12454
|
-
console.log(
|
|
12730
|
+
console.log(chalk26.green(` Installed (${names.length}): `) + chalk26.white(names.join(", ")));
|
|
12455
12731
|
}
|
|
12456
12732
|
if (notInstalled.length > 0) {
|
|
12457
|
-
console.log(
|
|
12733
|
+
console.log(chalk26.gray(` Available (${notInstalled.length}): `) + chalk26.gray(notInstalled.join(", ")));
|
|
12458
12734
|
}
|
|
12459
12735
|
console.log("");
|
|
12460
|
-
console.log(
|
|
12461
|
-
console.log(
|
|
12462
|
-
console.log(
|
|
12736
|
+
console.log(chalk26.cyan("\u{1F4A1} Commands:"));
|
|
12737
|
+
console.log(chalk26.white(' coherent chat "add a testimonial component"'));
|
|
12738
|
+
console.log(chalk26.white(' coherent chat --component "Header" "add a search button"'));
|
|
12463
12739
|
console.log("");
|
|
12464
12740
|
});
|
|
12465
12741
|
cmd.command("add <name>").description("Install a specific component").action(async (name) => {
|
|
12466
|
-
console.log(
|
|
12742
|
+
console.log(chalk26.yellow(`
|
|
12467
12743
|
\u{1F4A1} Use: coherent chat "add ${name} component"
|
|
12468
12744
|
`));
|
|
12469
12745
|
});
|
|
@@ -12476,25 +12752,25 @@ function createComponentsCommand() {
|
|
|
12476
12752
|
console.log(JSON.stringify(manifest, null, 2));
|
|
12477
12753
|
return;
|
|
12478
12754
|
}
|
|
12479
|
-
console.log(
|
|
12755
|
+
console.log(chalk26.bold("\n\u{1F4E6} Shared Components\n"));
|
|
12480
12756
|
if (manifest.shared.length === 0) {
|
|
12481
|
-
console.log(
|
|
12482
|
-
console.log(
|
|
12757
|
+
console.log(chalk26.yellow(" No shared components yet.\n"));
|
|
12758
|
+
console.log(chalk26.gray(' Create via chat: coherent chat "add a page with header and footer"\n'));
|
|
12483
12759
|
return;
|
|
12484
12760
|
}
|
|
12485
12761
|
const order = { layout: 0, section: 1, widget: 2 };
|
|
12486
12762
|
const sorted = [...manifest.shared].sort((a, b) => order[a.type] - order[b.type] || a.name.localeCompare(b.name));
|
|
12487
12763
|
sorted.forEach((entry) => {
|
|
12488
|
-
const usedIn = entry.usedIn.length === 0 ?
|
|
12489
|
-
console.log(
|
|
12764
|
+
const usedIn = entry.usedIn.length === 0 ? chalk26.gray("(not used yet)") : entry.usedIn.length === 1 && entry.usedIn[0] === "app/layout.tsx" ? chalk26.gray("layout.tsx (all pages)") : chalk26.gray(`used in: ${entry.usedIn.join(", ")}`);
|
|
12765
|
+
console.log(chalk26.cyan(` ${entry.id}`), chalk26.white(entry.name), chalk26.gray(entry.type));
|
|
12490
12766
|
if (opts.verbose) {
|
|
12491
|
-
console.log(
|
|
12492
|
-
if (entry.description) console.log(
|
|
12767
|
+
console.log(chalk26.gray(` file: ${entry.file}`));
|
|
12768
|
+
if (entry.description) console.log(chalk26.gray(` ${entry.description}`));
|
|
12493
12769
|
}
|
|
12494
|
-
console.log(
|
|
12770
|
+
console.log(chalk26.gray(` ${usedIn}`));
|
|
12495
12771
|
console.log("");
|
|
12496
12772
|
});
|
|
12497
|
-
console.log(
|
|
12773
|
+
console.log(chalk26.cyan("\u{1F4A1} Modify by ID:"), chalk26.white('coherent chat "in CID-001 add a search button"\n'));
|
|
12498
12774
|
});
|
|
12499
12775
|
sharedCmd.command("add <name>").description("Create a shared component (layout/section/widget) and register in manifest").option("-t, --type <type>", "Type: layout | section | widget", "layout").option("-d, --description <desc>", "Description").action(async (name, opts) => {
|
|
12500
12776
|
const project = findConfig();
|
|
@@ -12506,12 +12782,12 @@ function createComponentsCommand() {
|
|
|
12506
12782
|
description: opts.description,
|
|
12507
12783
|
usedIn: type === "layout" ? ["app/layout.tsx"] : []
|
|
12508
12784
|
});
|
|
12509
|
-
console.log(
|
|
12785
|
+
console.log(chalk26.green(`
|
|
12510
12786
|
\u2705 Created ${result.id} (${result.name}) at ${result.file}
|
|
12511
12787
|
`));
|
|
12512
12788
|
if (type === "layout") {
|
|
12513
12789
|
const updated = await integrateSharedLayoutIntoRootLayout3(project.root);
|
|
12514
|
-
if (updated) console.log(
|
|
12790
|
+
if (updated) console.log(chalk26.cyan(" Updated app/layout.tsx to use shared layout components.\n"));
|
|
12515
12791
|
}
|
|
12516
12792
|
const sharedPagePath = resolve14(project.root, "app/design-system/shared/page.tsx");
|
|
12517
12793
|
if (!existsSync23(sharedPagePath)) {
|
|
@@ -12521,23 +12797,23 @@ function createComponentsCommand() {
|
|
|
12521
12797
|
const config2 = dsm.getConfig();
|
|
12522
12798
|
const written = await writeDesignSystemFiles(project.root, config2, { sharedOnly: true });
|
|
12523
12799
|
if (written.length > 0) {
|
|
12524
|
-
console.log(
|
|
12800
|
+
console.log(chalk26.cyan(" Added Design System shared pages: /design-system/shared\n"));
|
|
12525
12801
|
}
|
|
12526
12802
|
} catch (e) {
|
|
12527
|
-
if (process.env.COHERENT_DEBUG === "1") console.error(
|
|
12803
|
+
if (process.env.COHERENT_DEBUG === "1") console.error(chalk26.dim("DS shared pages write failed:"), e);
|
|
12528
12804
|
}
|
|
12529
12805
|
}
|
|
12530
12806
|
try {
|
|
12531
12807
|
await writeCursorRules(project.root);
|
|
12532
12808
|
} catch (e) {
|
|
12533
|
-
if (process.env.COHERENT_DEBUG === "1") console.error(
|
|
12809
|
+
if (process.env.COHERENT_DEBUG === "1") console.error(chalk26.dim("Could not update .cursorrules:"), e);
|
|
12534
12810
|
}
|
|
12535
12811
|
});
|
|
12536
12812
|
return cmd;
|
|
12537
12813
|
}
|
|
12538
12814
|
|
|
12539
12815
|
// src/commands/import-cmd.ts
|
|
12540
|
-
import
|
|
12816
|
+
import chalk27 from "chalk";
|
|
12541
12817
|
import ora6 from "ora";
|
|
12542
12818
|
import { writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
|
|
12543
12819
|
import { resolve as resolve15, join as join18, dirname as dirname9 } from "path";
|
|
@@ -12642,25 +12918,25 @@ function createImportCommand() {
|
|
|
12642
12918
|
}
|
|
12643
12919
|
async function importFigmaAction(urlOrKey, opts) {
|
|
12644
12920
|
if (typeof urlOrKey !== "string" || !urlOrKey.trim()) {
|
|
12645
|
-
console.error(
|
|
12646
|
-
console.log(
|
|
12921
|
+
console.error(chalk27.red("\n\u274C Figma URL or file key is required.\n"));
|
|
12922
|
+
console.log(chalk27.dim(" Usage: coherent import figma <url-or-key> --token <your-token>\n"));
|
|
12647
12923
|
process.exit(1);
|
|
12648
12924
|
}
|
|
12649
12925
|
const token = opts.token ?? process.env.FIGMA_ACCESS_TOKEN ?? process.env.FIGMA_TOKEN;
|
|
12650
12926
|
if (!token || typeof token !== "string") {
|
|
12651
|
-
console.error(
|
|
12652
|
-
console.log(
|
|
12653
|
-
console.log(
|
|
12654
|
-
console.log(
|
|
12927
|
+
console.error(chalk27.red("\n\u274C Figma token required.\n"));
|
|
12928
|
+
console.log(chalk27.dim(" Use: coherent import figma <url-or-key> --token <your-token>"));
|
|
12929
|
+
console.log(chalk27.dim(" Or set FIGMA_ACCESS_TOKEN or FIGMA_TOKEN in your environment.\n"));
|
|
12930
|
+
console.log(chalk27.dim(" Get a token: Figma \u2192 Settings \u2192 Personal access tokens.\n"));
|
|
12655
12931
|
process.exit(1);
|
|
12656
12932
|
}
|
|
12657
12933
|
const generatePages = opts.pages !== false;
|
|
12658
12934
|
const dryRun = Boolean(opts.dryRun);
|
|
12659
12935
|
const fileKey = FigmaClient.extractFileKey(urlOrKey);
|
|
12660
12936
|
if (!fileKey) {
|
|
12661
|
-
console.error(
|
|
12662
|
-
console.log(
|
|
12663
|
-
console.log(
|
|
12937
|
+
console.error(chalk27.red("\n\u274C Invalid Figma URL or file key.\n"));
|
|
12938
|
+
console.log(chalk27.dim(" Use a URL like: https://www.figma.com/file/ABC123/MyDesign"));
|
|
12939
|
+
console.log(chalk27.dim(" Or the file key: ABC123\n"));
|
|
12664
12940
|
process.exit(1);
|
|
12665
12941
|
}
|
|
12666
12942
|
const project = findConfig();
|
|
@@ -12837,7 +13113,7 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
|
|
|
12837
13113
|
try {
|
|
12838
13114
|
await writeCursorRules(projectRoot);
|
|
12839
13115
|
} catch (e) {
|
|
12840
|
-
if (process.env.COHERENT_DEBUG === "1") console.error(
|
|
13116
|
+
if (process.env.COHERENT_DEBUG === "1") console.error(chalk27.dim("Could not update .cursorrules:"), e);
|
|
12841
13117
|
}
|
|
12842
13118
|
} else {
|
|
12843
13119
|
stats.filesWritten.push(DESIGN_SYSTEM_CONFIG_PATH);
|
|
@@ -12847,7 +13123,7 @@ export const config = ${JSON.stringify(fullConfig, null, 2)} as const
|
|
|
12847
13123
|
} catch (err) {
|
|
12848
13124
|
spinner.fail("Import failed");
|
|
12849
13125
|
const message = err instanceof Error ? err.message : String(err);
|
|
12850
|
-
console.error(
|
|
13126
|
+
console.error(chalk27.red("\n\u274C " + message + "\n"));
|
|
12851
13127
|
process.exit(1);
|
|
12852
13128
|
}
|
|
12853
13129
|
}
|
|
@@ -12855,36 +13131,36 @@ function printReport(stats, opts) {
|
|
|
12855
13131
|
const { dryRun, generatePages, fileName } = opts;
|
|
12856
13132
|
console.log("");
|
|
12857
13133
|
if (dryRun) {
|
|
12858
|
-
console.log(
|
|
13134
|
+
console.log(chalk27.yellow("\u2550\u2550\u2550 Dry run (no files written) \u2550\u2550\u2550"));
|
|
12859
13135
|
console.log("");
|
|
12860
13136
|
}
|
|
12861
|
-
console.log(
|
|
13137
|
+
console.log(chalk27.green("\u2705 Figma import complete"));
|
|
12862
13138
|
console.log("");
|
|
12863
|
-
console.log(
|
|
12864
|
-
console.log(
|
|
12865
|
-
console.log(
|
|
12866
|
-
console.log(
|
|
12867
|
-
console.log(
|
|
13139
|
+
console.log(chalk27.cyan(" Statistics"));
|
|
13140
|
+
console.log(chalk27.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
13141
|
+
console.log(chalk27.blue(` File: ${fileName}`));
|
|
13142
|
+
console.log(chalk27.blue(` Color styles: ${stats.colorStyles}`));
|
|
13143
|
+
console.log(chalk27.blue(` Text styles: ${stats.textStyles}`));
|
|
12868
13144
|
console.log(
|
|
12869
|
-
|
|
13145
|
+
chalk27.blue(` Components: ${stats.componentsTotal} (${stats.baseCount} \u2192 base, ${stats.sharedCount} \u2192 shared)`)
|
|
12870
13146
|
);
|
|
12871
|
-
console.log(
|
|
12872
|
-
console.log(
|
|
12873
|
-
console.log(
|
|
12874
|
-
console.log(
|
|
13147
|
+
console.log(chalk27.blue(` Pages: ${stats.pagesGenerated}${!generatePages ? " (skipped by --no-pages)" : ""}`));
|
|
13148
|
+
console.log(chalk27.blue(` design-system.config: ${stats.configUpdated ? "updated" : "\u2014"}`));
|
|
13149
|
+
console.log(chalk27.blue(` Layout (Header/Footer): ${stats.layoutIntegrated ? "integrated" : "\u2014"}`));
|
|
13150
|
+
console.log(chalk27.blue(` DS viewer files: ${stats.dsFilesWritten}`));
|
|
12875
13151
|
console.log(
|
|
12876
|
-
|
|
13152
|
+
chalk27.blue(` Total files ${dryRun ? "that would be written" : "written"}: ${stats.filesWritten.length}`)
|
|
12877
13153
|
);
|
|
12878
13154
|
console.log("");
|
|
12879
13155
|
if (stats.filesWritten.length > 0 && stats.filesWritten.length <= 30) {
|
|
12880
|
-
console.log(
|
|
12881
|
-
stats.filesWritten.forEach((f) => console.log(
|
|
13156
|
+
console.log(chalk27.dim(" Files:"));
|
|
13157
|
+
stats.filesWritten.forEach((f) => console.log(chalk27.dim(` ${f}`)));
|
|
12882
13158
|
console.log("");
|
|
12883
13159
|
}
|
|
12884
13160
|
}
|
|
12885
13161
|
|
|
12886
13162
|
// src/commands/ds.ts
|
|
12887
|
-
import
|
|
13163
|
+
import chalk28 from "chalk";
|
|
12888
13164
|
import ora7 from "ora";
|
|
12889
13165
|
import { DesignSystemManager as DesignSystemManager14 } from "@getcoherent/core";
|
|
12890
13166
|
async function dsRegenerateCommand() {
|
|
@@ -12899,16 +13175,16 @@ async function dsRegenerateCommand() {
|
|
|
12899
13175
|
const config2 = dsm.getConfig();
|
|
12900
13176
|
const written = await writeDesignSystemFiles(project.root, config2);
|
|
12901
13177
|
spinner.succeed(`Regenerated ${written.length} Design System file(s)`);
|
|
12902
|
-
console.log(
|
|
12903
|
-
console.log(
|
|
13178
|
+
console.log(chalk28.gray(" app/design-system/* and app/api/design-system/*\n"));
|
|
13179
|
+
console.log(chalk28.cyan(" Open /design-system in the app to view.\n"));
|
|
12904
13180
|
} catch (error) {
|
|
12905
|
-
console.error(
|
|
13181
|
+
console.error(chalk28.red("\u274C Command failed:"), error instanceof Error ? error.message : "Unknown error");
|
|
12906
13182
|
process.exit(1);
|
|
12907
13183
|
}
|
|
12908
13184
|
}
|
|
12909
13185
|
|
|
12910
13186
|
// src/commands/update.ts
|
|
12911
|
-
import
|
|
13187
|
+
import chalk29 from "chalk";
|
|
12912
13188
|
import ora8 from "ora";
|
|
12913
13189
|
import { readFileSync as readFileSync17, existsSync as existsSync25 } from "fs";
|
|
12914
13190
|
import { join as join19 } from "path";
|
|
@@ -12960,14 +13236,14 @@ async function updateCommand(opts) {
|
|
|
12960
13236
|
const projectVersion = config2.coherentVersion || "0.0.0";
|
|
12961
13237
|
if (compareSemver(projectVersion, CLI_VERSION4) === 0) {
|
|
12962
13238
|
spinner.succeed("Project is already up to date");
|
|
12963
|
-
console.log(
|
|
13239
|
+
console.log(chalk29.gray(` Version: v${CLI_VERSION4}
|
|
12964
13240
|
`));
|
|
12965
13241
|
return;
|
|
12966
13242
|
}
|
|
12967
13243
|
if (compareSemver(projectVersion, CLI_VERSION4) > 0) {
|
|
12968
13244
|
spinner.warn("Project was created with a newer CLI version");
|
|
12969
|
-
console.log(
|
|
12970
|
-
console.log(
|
|
13245
|
+
console.log(chalk29.yellow(` Project: v${projectVersion} \u2192 CLI: v${CLI_VERSION4}`));
|
|
13246
|
+
console.log(chalk29.yellow(" Update your CLI: npm install -g @getcoherent/cli@latest\n"));
|
|
12971
13247
|
return;
|
|
12972
13248
|
}
|
|
12973
13249
|
const report = {
|
|
@@ -13013,36 +13289,36 @@ async function updateCommand(opts) {
|
|
|
13013
13289
|
}
|
|
13014
13290
|
function printReport2(report) {
|
|
13015
13291
|
const from = report.fromVersion ? `v${report.fromVersion}` : "unknown";
|
|
13016
|
-
console.log(
|
|
13292
|
+
console.log(chalk29.green(`
|
|
13017
13293
|
\u2714 Project updated: ${from} \u2192 v${report.toVersion}
|
|
13018
13294
|
`));
|
|
13019
13295
|
if (report.overlayFiles > 0) {
|
|
13020
|
-
console.log(
|
|
13296
|
+
console.log(chalk29.white(` \u2714 Regenerated platform overlay (${report.overlayFiles} files)`));
|
|
13021
13297
|
}
|
|
13022
13298
|
if (report.migrationsApplied.length > 0) {
|
|
13023
13299
|
for (const desc of report.migrationsApplied) {
|
|
13024
|
-
console.log(
|
|
13300
|
+
console.log(chalk29.white(` \u2714 Migrated config: ${desc}`));
|
|
13025
13301
|
}
|
|
13026
13302
|
}
|
|
13027
13303
|
if (report.rulesUpdated) {
|
|
13028
|
-
console.log(
|
|
13304
|
+
console.log(chalk29.white(" \u2714 Updated .cursorrules and CLAUDE.md"));
|
|
13029
13305
|
}
|
|
13030
13306
|
if (report.missingCssVars.length > 0) {
|
|
13031
13307
|
console.log("");
|
|
13032
|
-
console.log(
|
|
13308
|
+
console.log(chalk29.yellow(" \u26A0 New CSS variables available in globals.css:"));
|
|
13033
13309
|
for (const v of report.missingCssVars.slice(0, 10)) {
|
|
13034
|
-
console.log(
|
|
13310
|
+
console.log(chalk29.gray(` ${v}`));
|
|
13035
13311
|
}
|
|
13036
13312
|
if (report.missingCssVars.length > 10) {
|
|
13037
|
-
console.log(
|
|
13313
|
+
console.log(chalk29.gray(` ... and ${report.missingCssVars.length - 10} more`));
|
|
13038
13314
|
}
|
|
13039
13315
|
console.log("");
|
|
13040
|
-
console.log(
|
|
13041
|
-
console.log(
|
|
13316
|
+
console.log(chalk29.cyan(" To add them automatically:"));
|
|
13317
|
+
console.log(chalk29.white(" coherent update --patch-globals\n"));
|
|
13042
13318
|
}
|
|
13043
13319
|
console.log("");
|
|
13044
|
-
console.log(
|
|
13045
|
-
console.log(
|
|
13320
|
+
console.log(chalk29.dim(" Your pages and components were NOT modified."));
|
|
13321
|
+
console.log(chalk29.dim(" Run `coherent check` to check existing pages against new rules.\n"));
|
|
13046
13322
|
}
|
|
13047
13323
|
var EXPECTED_CSS_VARS = [
|
|
13048
13324
|
"--background",
|
|
@@ -13126,7 +13402,7 @@ function patchGlobalsCss(projectRoot, missingVars) {
|
|
|
13126
13402
|
}
|
|
13127
13403
|
|
|
13128
13404
|
// src/commands/undo.ts
|
|
13129
|
-
import
|
|
13405
|
+
import chalk30 from "chalk";
|
|
13130
13406
|
async function undoCommand(options) {
|
|
13131
13407
|
try {
|
|
13132
13408
|
const project = findConfig();
|
|
@@ -13135,41 +13411,41 @@ async function undoCommand(options) {
|
|
|
13135
13411
|
const backups = listBackups(projectRoot);
|
|
13136
13412
|
if (options.list) {
|
|
13137
13413
|
if (backups.length === 0) {
|
|
13138
|
-
console.log(
|
|
13414
|
+
console.log(chalk30.yellow("No backups found."));
|
|
13139
13415
|
return;
|
|
13140
13416
|
}
|
|
13141
|
-
console.log(
|
|
13417
|
+
console.log(chalk30.bold("\n\u{1F4E6} Available backups:\n"));
|
|
13142
13418
|
for (const b of backups) {
|
|
13143
13419
|
const date = new Date(b.timestamp);
|
|
13144
13420
|
const timeStr = date.toLocaleString();
|
|
13145
|
-
console.log(
|
|
13146
|
-
console.log(
|
|
13421
|
+
console.log(chalk30.white(` ${b.name}`));
|
|
13422
|
+
console.log(chalk30.dim(` ${timeStr} \u2014 ${b.files} file(s)`));
|
|
13147
13423
|
console.log();
|
|
13148
13424
|
}
|
|
13149
13425
|
return;
|
|
13150
13426
|
}
|
|
13151
13427
|
if (backups.length === 0) {
|
|
13152
|
-
console.log(
|
|
13428
|
+
console.log(chalk30.yellow("No backups found. Nothing to undo."));
|
|
13153
13429
|
return;
|
|
13154
13430
|
}
|
|
13155
13431
|
const latest = backups[0];
|
|
13156
13432
|
const ok = restoreBackup(projectRoot, latest.name);
|
|
13157
13433
|
if (!ok) {
|
|
13158
|
-
console.log(
|
|
13434
|
+
console.log(chalk30.red("Failed to restore backup."));
|
|
13159
13435
|
return;
|
|
13160
13436
|
}
|
|
13161
|
-
console.log(
|
|
13162
|
-
console.log(
|
|
13163
|
-
console.log(
|
|
13164
|
-
console.log(
|
|
13437
|
+
console.log(chalk30.green("\n\u2705 Restored to previous state:\n"));
|
|
13438
|
+
console.log(chalk30.dim(` Snapshot: ${new Date(latest.timestamp).toLocaleString()}`));
|
|
13439
|
+
console.log(chalk30.dim(` Files: ${latest.files} restored`));
|
|
13440
|
+
console.log(chalk30.cyan("\n Run: coherent preview\n"));
|
|
13165
13441
|
} catch (error) {
|
|
13166
|
-
console.error(
|
|
13442
|
+
console.error(chalk30.red("\u274C Undo failed:"), error instanceof Error ? error.message : "Unknown error");
|
|
13167
13443
|
process.exit(1);
|
|
13168
13444
|
}
|
|
13169
13445
|
}
|
|
13170
13446
|
|
|
13171
13447
|
// src/commands/sync.ts
|
|
13172
|
-
import
|
|
13448
|
+
import chalk31 from "chalk";
|
|
13173
13449
|
import ora9 from "ora";
|
|
13174
13450
|
import { existsSync as existsSync26, readFileSync as readFileSync18 } from "fs";
|
|
13175
13451
|
import { join as join20, relative as relative5, dirname as dirname10 } from "path";
|
|
@@ -13407,7 +13683,7 @@ async function syncCommand(options = {}) {
|
|
|
13407
13683
|
const doTokens = runAll || options.tokens === true;
|
|
13408
13684
|
const doComponents = runAll || options.components === true;
|
|
13409
13685
|
const doPatterns = runAll || options.patterns === true;
|
|
13410
|
-
if (dryRun) console.log(
|
|
13686
|
+
if (dryRun) console.log(chalk31.yellow(" [dry-run] No files will be written\n"));
|
|
13411
13687
|
const spinner = ora9("Scanning project files...").start();
|
|
13412
13688
|
try {
|
|
13413
13689
|
const appDir = join20(project.root, "app");
|
|
@@ -13548,84 +13824,84 @@ async function syncCommand(options = {}) {
|
|
|
13548
13824
|
spinner.succeed("Updated .cursorrules and CLAUDE.md");
|
|
13549
13825
|
}
|
|
13550
13826
|
console.log("");
|
|
13551
|
-
console.log(
|
|
13827
|
+
console.log(chalk31.green(`\u2705 Design System ${dryRun ? "analyzed" : "synced"} with actual code
|
|
13552
13828
|
`));
|
|
13553
|
-
console.log(
|
|
13829
|
+
console.log(chalk31.blue("\u{1F4C4} Pages:"));
|
|
13554
13830
|
for (const page of discoveredPages) {
|
|
13555
13831
|
const a = analyzePageCode(page.code);
|
|
13556
13832
|
const comps = Object.entries(a.componentUsage || {}).filter(([, c]) => c > 0).map(([n]) => n);
|
|
13557
|
-
console.log(
|
|
13558
|
-
if (comps.length > 0) console.log(
|
|
13559
|
-
if (a.sections?.length) console.log(
|
|
13833
|
+
console.log(chalk31.gray(` ${page.route} \u2014 ${page.name}`));
|
|
13834
|
+
if (comps.length > 0) console.log(chalk31.gray(` Components: ${comps.join(", ")}`));
|
|
13835
|
+
if (a.sections?.length) console.log(chalk31.gray(` Sections: ${a.sections.map((s) => s.name).join(", ")}`));
|
|
13560
13836
|
}
|
|
13561
13837
|
if (doTokens && extractedTokens) {
|
|
13562
13838
|
console.log("");
|
|
13563
|
-
console.log(
|
|
13839
|
+
console.log(chalk31.blue("\u{1F3A8} Design Tokens (from globals.css):"));
|
|
13564
13840
|
const lc = Object.keys(extractedTokens.colors.light).length;
|
|
13565
13841
|
const dc = Object.keys(extractedTokens.colors.dark).length;
|
|
13566
|
-
console.log(
|
|
13567
|
-
console.log(
|
|
13568
|
-
if (extractedTokens.radius) console.log(
|
|
13842
|
+
console.log(chalk31.gray(` Light: ${lc} variables | Dark: ${dc} variables`));
|
|
13843
|
+
console.log(chalk31.gray(` Default mode: ${extractedTokens.defaultMode}`));
|
|
13844
|
+
if (extractedTokens.radius) console.log(chalk31.gray(` Border radius: ${extractedTokens.radius}`));
|
|
13569
13845
|
}
|
|
13570
13846
|
if (doComponents && reconcileResult) {
|
|
13571
13847
|
console.log("");
|
|
13572
|
-
console.log(
|
|
13848
|
+
console.log(chalk31.blue("\u{1F9E9} Shared Components:"));
|
|
13573
13849
|
for (const r of reconcileResult.removed) {
|
|
13574
|
-
console.log(
|
|
13850
|
+
console.log(chalk31.red(` \u{1F5D1} Removed ${r.id} (${r.name}) \u2014 ${r.reason}`));
|
|
13575
13851
|
}
|
|
13576
13852
|
for (const u of reconcileResult.updated) {
|
|
13577
|
-
console.log(
|
|
13853
|
+
console.log(chalk31.cyan(` \u{1F4DD} Updated ${u.id} ${u.field}: ${u.from} \u2192 ${u.to}`));
|
|
13578
13854
|
}
|
|
13579
13855
|
for (const a of reconcileResult.added) {
|
|
13580
|
-
console.log(
|
|
13856
|
+
console.log(chalk31.green(` \u2728 Added ${a.id} (${a.name}) \u2014 ${a.file} (${a.type})`));
|
|
13581
13857
|
}
|
|
13582
13858
|
for (const w of reconcileResult.warnings) {
|
|
13583
|
-
console.log(
|
|
13584
|
-
console.log(
|
|
13859
|
+
console.log(chalk31.yellow(` \u26A0 ${w.message}`));
|
|
13860
|
+
console.log(chalk31.dim(` ${w.suggestion}`));
|
|
13585
13861
|
}
|
|
13586
13862
|
if (reconcileResult.removed.length === 0 && reconcileResult.updated.length === 0 && reconcileResult.added.length === 0 && reconcileResult.warnings.length === 0) {
|
|
13587
|
-
console.log(
|
|
13863
|
+
console.log(chalk31.gray(" All components consistent \u2713"));
|
|
13588
13864
|
}
|
|
13589
13865
|
}
|
|
13590
13866
|
if (doPatterns && Object.keys(stylePatterns).length > 0) {
|
|
13591
13867
|
console.log("");
|
|
13592
|
-
console.log(
|
|
13593
|
-
if (stylePatterns.card) console.log(
|
|
13594
|
-
if (stylePatterns.section) console.log(
|
|
13595
|
-
if (stylePatterns.terminal) console.log(
|
|
13596
|
-
if (stylePatterns.iconContainer) console.log(
|
|
13868
|
+
console.log(chalk31.blue("\u{1F4D0} Style Patterns:"));
|
|
13869
|
+
if (stylePatterns.card) console.log(chalk31.gray(` Cards: ${stylePatterns.card.slice(0, 80)}`));
|
|
13870
|
+
if (stylePatterns.section) console.log(chalk31.gray(` Sections: ${stylePatterns.section}`));
|
|
13871
|
+
if (stylePatterns.terminal) console.log(chalk31.gray(` Terminal: ${stylePatterns.terminal.slice(0, 80)}`));
|
|
13872
|
+
if (stylePatterns.iconContainer) console.log(chalk31.gray(` Icons: ${stylePatterns.iconContainer.slice(0, 80)}`));
|
|
13597
13873
|
if (stylePatterns.heroHeadline)
|
|
13598
|
-
console.log(
|
|
13874
|
+
console.log(chalk31.gray(` Hero headline: ${stylePatterns.heroHeadline.slice(0, 80)}`));
|
|
13599
13875
|
if (stylePatterns.sectionTitle)
|
|
13600
|
-
console.log(
|
|
13876
|
+
console.log(chalk31.gray(` Section title: ${stylePatterns.sectionTitle.slice(0, 80)}`));
|
|
13601
13877
|
}
|
|
13602
13878
|
const tokenUsage = extractActualTokenUsage(allPageCode);
|
|
13603
13879
|
if (tokenUsage.colors.length > 0) {
|
|
13604
13880
|
console.log("");
|
|
13605
|
-
console.log(
|
|
13881
|
+
console.log(chalk31.blue("\u{1F3F7}\uFE0F Actual token usage (from classNames):"));
|
|
13606
13882
|
console.log(
|
|
13607
|
-
|
|
13883
|
+
chalk31.gray(
|
|
13608
13884
|
` Colors: ${tokenUsage.colors.slice(0, 12).join(", ")}${tokenUsage.colors.length > 12 ? ` (+${tokenUsage.colors.length - 12})` : ""}`
|
|
13609
13885
|
)
|
|
13610
13886
|
);
|
|
13611
13887
|
console.log(
|
|
13612
|
-
|
|
13888
|
+
chalk31.gray(
|
|
13613
13889
|
` Typography: ${tokenUsage.typography.slice(0, 8).join(", ")}${tokenUsage.typography.length > 8 ? ` (+${tokenUsage.typography.length - 8})` : ""}`
|
|
13614
13890
|
)
|
|
13615
13891
|
);
|
|
13616
|
-
console.log(
|
|
13892
|
+
console.log(chalk31.gray(` Radius: ${tokenUsage.borderRadius.join(", ")}`));
|
|
13617
13893
|
}
|
|
13618
13894
|
const reusable = extractReusablePatterns(allPageCode);
|
|
13619
13895
|
if (reusable.length > 0) {
|
|
13620
13896
|
console.log("");
|
|
13621
|
-
console.log(
|
|
13897
|
+
console.log(chalk31.blue(`\u{1F501} Repeating patterns (${reusable.length} \u2014 potential reusable components):`));
|
|
13622
13898
|
for (const p of reusable.slice(0, 5)) {
|
|
13623
|
-
console.log(
|
|
13899
|
+
console.log(chalk31.gray(` \xD7${p.count}: ${p.sample}${p.sample.length < p.pattern.length ? "..." : ""}`));
|
|
13624
13900
|
}
|
|
13625
13901
|
}
|
|
13626
13902
|
console.log("");
|
|
13627
13903
|
if (!dryRun) {
|
|
13628
|
-
console.log(
|
|
13904
|
+
console.log(chalk31.cyan(" Open /design-system in the app to see the updated view."));
|
|
13629
13905
|
}
|
|
13630
13906
|
console.log("");
|
|
13631
13907
|
} catch (err) {
|
|
@@ -13636,7 +13912,7 @@ async function syncCommand(options = {}) {
|
|
|
13636
13912
|
}
|
|
13637
13913
|
|
|
13638
13914
|
// src/commands/migrate.ts
|
|
13639
|
-
import
|
|
13915
|
+
import chalk32 from "chalk";
|
|
13640
13916
|
import ora10 from "ora";
|
|
13641
13917
|
import { existsSync as existsSync27, mkdirSync as mkdirSync8, cpSync, rmSync as rmSync6, writeFileSync as writeFileSync12, readFileSync as readFileSync19, readdirSync as readdirSync9 } from "fs";
|
|
13642
13918
|
import { join as join21 } from "path";
|
|
@@ -13713,13 +13989,13 @@ async function migrateAction(options) {
|
|
|
13713
13989
|
}
|
|
13714
13990
|
const guard = guardPath(projectRoot);
|
|
13715
13991
|
if (existsSync27(guard)) {
|
|
13716
|
-
console.log(
|
|
13717
|
-
console.log(
|
|
13992
|
+
console.log(chalk32.yellow("A migration is already in progress."));
|
|
13993
|
+
console.log(chalk32.dim("Run `coherent migrate --rollback` to undo, or delete .coherent/migration-in-progress"));
|
|
13718
13994
|
return;
|
|
13719
13995
|
}
|
|
13720
13996
|
const uiDir = join21(projectRoot, "components", "ui");
|
|
13721
13997
|
if (!existsSync27(uiDir)) {
|
|
13722
|
-
console.log(
|
|
13998
|
+
console.log(chalk32.yellow("No components/ui directory found. Nothing to migrate."));
|
|
13723
13999
|
return;
|
|
13724
14000
|
}
|
|
13725
14001
|
const provider = getComponentProvider();
|
|
@@ -13727,16 +14003,16 @@ async function migrateAction(options) {
|
|
|
13727
14003
|
const files = readdirSync9(uiDir).filter((f) => f.endsWith(".tsx"));
|
|
13728
14004
|
const migratable = files.map((f) => f.replace(".tsx", "")).filter((id) => managedIds.has(id));
|
|
13729
14005
|
if (migratable.length === 0) {
|
|
13730
|
-
console.log(
|
|
14006
|
+
console.log(chalk32.green("All components are already up to date."));
|
|
13731
14007
|
return;
|
|
13732
14008
|
}
|
|
13733
|
-
console.log(
|
|
14009
|
+
console.log(chalk32.cyan(`
|
|
13734
14010
|
Found ${migratable.length} component(s) to migrate:`));
|
|
13735
14011
|
for (const id of migratable) {
|
|
13736
|
-
console.log(
|
|
14012
|
+
console.log(chalk32.dim(` - ${id}`));
|
|
13737
14013
|
}
|
|
13738
14014
|
if (options.dryRun) {
|
|
13739
|
-
console.log(
|
|
14015
|
+
console.log(chalk32.yellow("\n[dry-run] No changes applied."));
|
|
13740
14016
|
return;
|
|
13741
14017
|
}
|
|
13742
14018
|
const spinner = ora10("Migrating components...").start();
|
|
@@ -13753,12 +14029,12 @@ Found ${migratable.length} component(s) to migrate:`));
|
|
|
13753
14029
|
if (result.success) {
|
|
13754
14030
|
migrated++;
|
|
13755
14031
|
} else {
|
|
13756
|
-
console.warn(
|
|
14032
|
+
console.warn(chalk32.yellow(` \u26A0 Failed to migrate ${id}`));
|
|
13757
14033
|
}
|
|
13758
14034
|
}
|
|
13759
14035
|
clearGuard(projectRoot);
|
|
13760
14036
|
spinner.succeed(`Migrated ${migrated}/${migratable.length} components to real shadcn/ui`);
|
|
13761
|
-
console.log(
|
|
14037
|
+
console.log(chalk32.dim(` Backup saved to: ${backup}`));
|
|
13762
14038
|
} catch (err) {
|
|
13763
14039
|
spinner.fail("Migration failed \u2014 rolling back");
|
|
13764
14040
|
rollback(projectRoot);
|
|
@@ -13770,7 +14046,7 @@ Found ${migratable.length} component(s) to migrate:`));
|
|
|
13770
14046
|
import { existsSync as existsSync28, mkdirSync as mkdirSync9, readFileSync as readFileSync20, writeFileSync as writeFileSync13 } from "fs";
|
|
13771
14047
|
import { join as join22 } from "path";
|
|
13772
14048
|
import { homedir } from "os";
|
|
13773
|
-
import
|
|
14049
|
+
import chalk33 from "chalk";
|
|
13774
14050
|
import { CLI_VERSION as CLI_VERSION5 } from "@getcoherent/core";
|
|
13775
14051
|
var DEBUG5 = process.env.COHERENT_DEBUG === "1";
|
|
13776
14052
|
var PACKAGE_NAME = "@getcoherent/cli";
|
|
@@ -13841,8 +14117,8 @@ async function checkForUpdates() {
|
|
|
13841
14117
|
}
|
|
13842
14118
|
function printUpdateNotice(latest) {
|
|
13843
14119
|
console.log(
|
|
13844
|
-
|
|
13845
|
-
\u2B06 Update available: v${CLI_VERSION5} \u2192 v${latest}`) +
|
|
14120
|
+
chalk33.yellow(`
|
|
14121
|
+
\u2B06 Update available: v${CLI_VERSION5} \u2192 v${latest}`) + chalk33.dim(`
|
|
13846
14122
|
Run: npm update -g ${PACKAGE_NAME}
|
|
13847
14123
|
`)
|
|
13848
14124
|
);
|