@getcoherent/cli 0.5.4 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.js +195 -95
  2. package/package.json +10 -10
  3. package/LICENSE +0 -21
package/dist/index.js CHANGED
@@ -3909,6 +3909,7 @@ DROPDOWN MENU:
3909
3909
  - Item text: text-sm. Icon before text: size-4 mr-2.
3910
3910
  - Group related items with DropdownMenuSeparator.
3911
3911
  - Destructive item: className="text-destructive" at bottom, separated.
3912
+ - NON-DESTRUCTIVE items: NEVER apply text color classes. Use default text-foreground. No text-amber, text-orange, text-yellow on menu items.
3912
3913
  - Keyboard shortcut hint: <DropdownMenuShortcut>\u2318K</DropdownMenuShortcut>.
3913
3914
  - Max items without scroll: 8. For more, use Command palette.
3914
3915
  - Trigger: Button variant="ghost" size="icon" for icon-only, variant="outline" for labeled.
@@ -4284,7 +4285,8 @@ async function parseModification(message, context, provider = "auto", options) {
4284
4285
  const prompt2 = buildPlanOnlyPrompt(message, context.config);
4285
4286
  const raw2 = await ai.parseModification(prompt2);
4286
4287
  const requestsArray2 = Array.isArray(raw2) ? raw2 : raw2?.requests ?? [];
4287
- return { requests: requestsArray2, uxRecommendations: void 0 };
4288
+ const navigation = !Array.isArray(raw2) && raw2?.navigation ? raw2.navigation : void 0;
4289
+ return { requests: requestsArray2, uxRecommendations: void 0, navigation };
4288
4290
  }
4289
4291
  const componentRegistry = buildComponentRegistry(context.componentManager);
4290
4292
  let enhancedMessage = message;
@@ -4341,7 +4343,10 @@ Return ONLY a JSON object with this structure (no pageCode, no sections, no cont
4341
4343
  {
4342
4344
  "requests": [
4343
4345
  { "type": "add-page", "target": "new", "changes": { "id": "page-id", "name": "Page Name", "route": "/page-route" } }
4344
- ]
4346
+ ],
4347
+ "navigation": {
4348
+ "type": "header"
4349
+ }
4345
4350
  }
4346
4351
 
4347
4352
  Rules:
@@ -4349,6 +4354,10 @@ Rules:
4349
4354
  - Route must start with /
4350
4355
  - Keep response under 500 tokens
4351
4356
  - Do NOT include pageCode, sections, or any other fields
4357
+ - Navigation type: Detect from user's request and include in response:
4358
+ * "sidebar" \u2014 if user mentions sidebar, side menu, left panel, admin panel, or app has 6+ main sections
4359
+ * "header" \u2014 if user mentions top navigation, header nav, or app is simple (< 6 sections). This is the default.
4360
+ * "both" \u2014 if complex multi-level app needs both header and sidebar navigation
4352
4361
  - Include ALL pages the user explicitly requested
4353
4362
  - ALSO include logically related pages that a real app would need. For example:
4354
4363
  * If there is a catalog/listing page, add a detail page (e.g. /products \u2192 /products/[id])
@@ -4487,6 +4496,7 @@ LAYOUT CONTRACT (CRITICAL \u2014 prevents duplicate navigation and footer):
4487
4496
  - The app has a root layout (app/layout.tsx) that renders a shared Header and Footer.
4488
4497
  - Pages are rendered INSIDE this layout, between the Header and Footer.
4489
4498
  - NEVER include <header>, <nav>, or <footer> elements in pageCode. Also do NOT add a footer-like section at the bottom (no "\xA9 2024", no site links, no logo + nav links at the bottom).
4499
+ - NEVER generate a sidebar panel, navigation column, or left-side navigation inside pageCode. Sidebar navigation is handled by the layout system via shared Sidebar component. If the user mentions "sidebar", it will be rendered by app/(app)/layout.tsx \u2014 do NOT recreate it in page code.
4490
4500
  - If the page needs sub-navigation (tabs, breadcrumbs, sidebar nav), use elements like <div role="tablist"> or <aside> \u2014 NOT <header>, <nav>, or <footer>.
4491
4501
  - Do NOT add any navigation bars, logo headers, site-wide menus, or site footers to pages. The layout provides all of these.
4492
4502
 
@@ -4503,7 +4513,7 @@ PAGE CONTENT (CRITICAL \u2014 prevents empty or duplicate pages):
4503
4513
  - Every page MUST have substantial content. NEVER generate a page with only metadata and an empty <main> element.
4504
4514
  - NEVER create an inline preview/demo of another page (e.g., embedding a "dashboard view" inside the landing page with a toggle). Each page should be its own route.
4505
4515
  - NEVER create a single-page app (SPA) that renders multiple views via useState. Each view must be a separate Next.js page with its own route.
4506
- - The home page (route "/") should be a simple redirect using next/navigation redirect('/dashboard') \u2014 OR a standalone landing page. NEVER a multi-view SPA.
4516
+ - The home page (route "/"): When BOTH "/" and "/dashboard" exist, "/" MUST be a full landing page with hero section, features, pricing, and CTA buttons linking to /login and /register \u2014 NOT a redirect. When "/" is the ONLY main page, it can be a redirect to /dashboard. NEVER a multi-view SPA.
4507
4517
  - Landing pages should link to app pages via <Link href="/dashboard">, NOT via useState toggles that render inline content.
4508
4518
 
4509
4519
  pageCode rules (shadcn/ui blocks quality):
@@ -4515,6 +4525,7 @@ pageCode rules (shadcn/ui blocks quality):
4515
4525
  - No placeholders: real contextual copy only. Use the EXACT text, language, and content from the user's request.
4516
4526
  - IMAGES: For avatar/profile photos, use https://i.pravatar.cc/150?u=<unique-seed> (e.g. ?u=sarah.johnson). For hero/product images, use https://picsum.photos/800/400?random=N. Use standard <img> tags with className, NOT Next.js <Image>. Always provide alt text.
4517
4527
  - BUTTON + LINK: The Button component supports asChild prop. To make a button that navigates, use <Button asChild><Link href="/path"><Plus className="size-4" /> Label</Link></Button>. Never nest <button> inside <Link> or vice versa without asChild.
4528
+ - DOM NESTING: NEVER nest interactive elements. No <Button> inside <Link>, no <a> inside <a>, no <button> inside <button>. For clickable cards containing internal buttons, use onClick on the card wrapper \u2014 NOT <Link> wrapping the entire card.
4518
4529
  - Hover/focus on every interactive element (hover:bg-muted, focus-visible:ring-2 focus-visible:ring-ring).
4519
4530
  - LANGUAGE: Match the language of the user's request. English request \u2192 English page. Russian request \u2192 Russian page. Never switch languages.
4520
4531
  - NEVER use native HTML <select> or <option>. Always use Select, SelectTrigger, SelectValue, SelectContent, SelectItem from @/components/ui/select.
@@ -4705,7 +4716,7 @@ var AUTH_LAYOUT = `export default function AuthLayout({
4705
4716
  children: React.ReactNode
4706
4717
  }) {
4707
4718
  return (
4708
- <div className="min-h-svh bg-muted">
4719
+ <div className="min-h-svh bg-muted flex items-center justify-center p-4">
4709
4720
  {children}
4710
4721
  </div>
4711
4722
  )
@@ -5308,13 +5319,14 @@ function validatePageQuality(code, validRoutes) {
5308
5319
  });
5309
5320
  }
5310
5321
  const headingLevels = [...code.matchAll(/<h([1-6])[\s>]/g)].map((m) => parseInt(m[1]));
5322
+ const hasCardContext = /\bCard\b|\bCardTitle\b|\bCardHeader\b/.test(code);
5311
5323
  for (let i = 1; i < headingLevels.length; i++) {
5312
5324
  if (headingLevels[i] > headingLevels[i - 1] + 1) {
5313
5325
  issues.push({
5314
5326
  line: 0,
5315
5327
  type: "SKIPPED_HEADING",
5316
5328
  message: `Heading level skipped: h${headingLevels[i - 1]} \u2192 h${headingLevels[i]} \u2014 don't skip levels`,
5317
- severity: "warning"
5329
+ severity: hasCardContext ? "info" : "warning"
5318
5330
  });
5319
5331
  break;
5320
5332
  }
@@ -5441,8 +5453,115 @@ function validatePageQuality(code, validRoutes) {
5441
5453
  }
5442
5454
  }
5443
5455
  }
5456
+ const linkBlockRe = /<(?:Link|a)\b[^>]*>[\s\S]*?<\/(?:Link|a)>/g;
5457
+ let linkMatch;
5458
+ while ((linkMatch = linkBlockRe.exec(code)) !== null) {
5459
+ const block = linkMatch[0];
5460
+ if (/<(?:Button|button)\b/.test(block) && !/asChild/.test(block)) {
5461
+ issues.push({
5462
+ line: 0,
5463
+ type: "NESTED_INTERACTIVE",
5464
+ message: "Button inside Link without asChild \u2014 causes DOM nesting error. Use <Button asChild><Link>...</Link></Button> instead",
5465
+ severity: "error"
5466
+ });
5467
+ break;
5468
+ }
5469
+ }
5470
+ const nestedAnchorRe = /<a\b[^>]*>[\s\S]*?<a\b/;
5471
+ if (nestedAnchorRe.test(code)) {
5472
+ issues.push({
5473
+ line: 0,
5474
+ type: "NESTED_INTERACTIVE",
5475
+ message: "Nested <a> tags \u2014 causes DOM nesting error. Remove inner anchor or restructure",
5476
+ severity: "error"
5477
+ });
5478
+ }
5444
5479
  return issues;
5445
5480
  }
5481
+ function replaceRawColors(classes, colorMap) {
5482
+ let changed = false;
5483
+ let result = classes;
5484
+ const accentColorRe = /\b(bg|text|border)-(emerald|blue|violet|indigo|purple|teal|cyan|sky|rose|amber|red|green|yellow|pink|orange|fuchsia|lime)-(\d+)\b/g;
5485
+ result = result.replace(accentColorRe, (m, prefix, color, shade) => {
5486
+ if (colorMap[m]) {
5487
+ changed = true;
5488
+ return colorMap[m];
5489
+ }
5490
+ const n = parseInt(shade);
5491
+ const isDestructive = color === "red";
5492
+ if (prefix === "bg") {
5493
+ if (n >= 500 && n <= 700) {
5494
+ changed = true;
5495
+ return isDestructive ? "bg-destructive" : "bg-primary";
5496
+ }
5497
+ if (n >= 100 && n <= 200) {
5498
+ changed = true;
5499
+ return isDestructive ? "bg-destructive/10" : "bg-primary/10";
5500
+ }
5501
+ if (n >= 300 && n <= 400) {
5502
+ changed = true;
5503
+ return isDestructive ? "bg-destructive/20" : "bg-primary/20";
5504
+ }
5505
+ if (n >= 800) {
5506
+ changed = true;
5507
+ return "bg-muted";
5508
+ }
5509
+ }
5510
+ if (prefix === "text") {
5511
+ if (n >= 400 && n <= 600) {
5512
+ changed = true;
5513
+ return isDestructive ? "text-destructive" : "text-primary";
5514
+ }
5515
+ if (n >= 100 && n <= 300) {
5516
+ changed = true;
5517
+ return "text-foreground";
5518
+ }
5519
+ if (n >= 700) {
5520
+ changed = true;
5521
+ return "text-foreground";
5522
+ }
5523
+ }
5524
+ if (prefix === "border") {
5525
+ changed = true;
5526
+ return isDestructive ? "border-destructive" : "border-primary";
5527
+ }
5528
+ return m;
5529
+ });
5530
+ const neutralColorRe = /\b(bg|text|border)-(zinc|slate|gray|neutral|stone)-(\d+)\b/g;
5531
+ result = result.replace(neutralColorRe, (m, prefix, _color, shade) => {
5532
+ if (colorMap[m]) {
5533
+ changed = true;
5534
+ return colorMap[m];
5535
+ }
5536
+ const n = parseInt(shade);
5537
+ if (prefix === "bg") {
5538
+ if (n >= 800) {
5539
+ changed = true;
5540
+ return "bg-background";
5541
+ }
5542
+ if (n >= 100 && n <= 300) {
5543
+ changed = true;
5544
+ return "bg-muted";
5545
+ }
5546
+ }
5547
+ if (prefix === "text") {
5548
+ if (n >= 100 && n <= 300) {
5549
+ changed = true;
5550
+ return "text-foreground";
5551
+ }
5552
+ if (n >= 400 && n <= 600) {
5553
+ changed = true;
5554
+ return "text-muted-foreground";
5555
+ }
5556
+ }
5557
+ if (prefix === "border") {
5558
+ changed = true;
5559
+ return "border-border";
5560
+ }
5561
+ return m;
5562
+ });
5563
+ return { result, changed };
5564
+ }
5446
5565
  async function autoFixCode(code) {
5447
5566
  const fixes = [];
5448
5567
  let fixed = code;
@@ -5596,89 +5715,34 @@ ${fixed}`;
5596
5715
  fixed = fixed.replace(/className="([^"]*)"/g, (fullMatch, classes, offset) => {
5597
5716
  if (isCodeContext(classes)) return fullMatch;
5598
5717
  if (isInsideTerminalBlock(offset)) return fullMatch;
5599
- let result = classes;
5600
- const accentColorRe = /\b(bg|text|border)-(emerald|blue|violet|indigo|purple|teal|cyan|sky|rose|amber|red|green|yellow|pink|orange|fuchsia|lime)-(\d+)\b/g;
5601
- result = result.replace(accentColorRe, (m, prefix, color, shade) => {
5602
- if (colorMap[m]) {
5603
- hadColorFix = true;
5604
- return colorMap[m];
5605
- }
5606
- const n = parseInt(shade);
5607
- const isDestructive = color === "red";
5608
- if (prefix === "bg") {
5609
- if (n >= 500 && n <= 700) {
5610
- hadColorFix = true;
5611
- return isDestructive ? "bg-destructive" : "bg-primary";
5612
- }
5613
- if (n >= 100 && n <= 200) {
5614
- hadColorFix = true;
5615
- return isDestructive ? "bg-destructive/10" : "bg-primary/10";
5616
- }
5617
- if (n >= 300 && n <= 400) {
5618
- hadColorFix = true;
5619
- return isDestructive ? "bg-destructive/20" : "bg-primary/20";
5620
- }
5621
- if (n >= 800) {
5622
- hadColorFix = true;
5623
- return "bg-muted";
5624
- }
5625
- }
5626
- if (prefix === "text") {
5627
- if (n >= 400 && n <= 600) {
5628
- hadColorFix = true;
5629
- return isDestructive ? "text-destructive" : "text-primary";
5630
- }
5631
- if (n >= 100 && n <= 300) {
5632
- hadColorFix = true;
5633
- return "text-foreground";
5634
- }
5635
- if (n >= 700) {
5636
- hadColorFix = true;
5637
- return "text-foreground";
5638
- }
5639
- }
5640
- if (prefix === "border") {
5641
- hadColorFix = true;
5642
- return isDestructive ? "border-destructive" : "border-primary";
5643
- }
5644
- return m;
5645
- });
5646
- const neutralColorRe = /\b(bg|text|border)-(zinc|slate|gray|neutral|stone)-(\d+)\b/g;
5647
- result = result.replace(neutralColorRe, (m, prefix, _color, shade) => {
5648
- if (colorMap[m]) {
5649
- hadColorFix = true;
5650
- return colorMap[m];
5651
- }
5652
- const n = parseInt(shade);
5653
- if (prefix === "bg") {
5654
- if (n >= 800) {
5655
- hadColorFix = true;
5656
- return "bg-background";
5657
- }
5658
- if (n >= 100 && n <= 300) {
5659
- hadColorFix = true;
5660
- return "bg-muted";
5661
- }
5662
- }
5663
- if (prefix === "text") {
5664
- if (n >= 100 && n <= 300) {
5665
- hadColorFix = true;
5666
- return "text-foreground";
5667
- }
5668
- if (n >= 400 && n <= 600) {
5669
- hadColorFix = true;
5670
- return "text-muted-foreground";
5671
- }
5672
- }
5673
- if (prefix === "border") {
5674
- hadColorFix = true;
5675
- return "border-border";
5676
- }
5677
- return m;
5678
- });
5718
+ const { result, changed } = replaceRawColors(classes, colorMap);
5719
+ if (changed) hadColorFix = true;
5679
5720
  if (result !== classes) return `className="${result}"`;
5680
5721
  return fullMatch;
5681
5722
  });
5723
+ fixed = fixed.replace(/(?:cn|clsx|cva)\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g, (fullMatch, args) => {
5724
+ const replaced = args.replace(/"([^"]*)"/g, (_qm, inner) => {
5725
+ const { result, changed } = replaceRawColors(inner, colorMap);
5726
+ if (changed) hadColorFix = true;
5727
+ return `"${result}"`;
5728
+ });
5729
+ if (replaced !== args) return fullMatch.replace(args, replaced);
5730
+ return fullMatch;
5731
+ });
5732
+ fixed = fixed.replace(/className='([^']*)'/g, (fullMatch, classes, offset) => {
5733
+ if (isCodeContext(classes)) return fullMatch;
5734
+ if (isInsideTerminalBlock(offset)) return fullMatch;
5735
+ const { result, changed } = replaceRawColors(classes, colorMap);
5736
+ if (changed) hadColorFix = true;
5737
+ if (result !== classes) return `className='${result}'`;
5738
+ return fullMatch;
5739
+ });
5740
+ fixed = fixed.replace(/className=\{`([^`]*)`\}/g, (fullMatch, inner) => {
5741
+ const { result, changed } = replaceRawColors(inner, colorMap);
5742
+ if (changed) hadColorFix = true;
5743
+ if (result !== inner) return `className={\`${result}\`}`;
5744
+ return fullMatch;
5745
+ });
5682
5746
  if (hadColorFix) fixes.push("raw colors \u2192 semantic tokens");
5683
5747
  const selectRe = /<select\b[^>]*>([\s\S]*?)<\/select>/g;
5684
5748
  let hadSelectFix = false;
@@ -5812,6 +5876,17 @@ ${selectImport}`
5812
5876
  }
5813
5877
  }
5814
5878
  }
5879
+ const linkWithButtonRe = /(<Link\b[^>]*>)\s*(<Button\b(?![^>]*asChild)[^>]*>)([\s\S]*?)<\/Button>\s*<\/Link>/g;
5880
+ const beforeLinkFix = fixed;
5881
+ fixed = fixed.replace(linkWithButtonRe, (_match, linkOpen, buttonOpen, inner) => {
5882
+ const hrefMatch = linkOpen.match(/href="([^"]*)"/);
5883
+ const href = hrefMatch ? hrefMatch[1] : "/";
5884
+ const buttonWithAsChild = buttonOpen.replace("<Button", "<Button asChild");
5885
+ return `${buttonWithAsChild}<Link href="${href}">${inner.trim()}</Link></Button>`;
5886
+ });
5887
+ if (fixed !== beforeLinkFix) {
5888
+ fixes.push("Link>Button \u2192 Button asChild>Link (DOM nesting fix)");
5889
+ }
5815
5890
  fixed = fixed.replace(/className="([^"]*)"/g, (_match, inner) => {
5816
5891
  const cleaned = inner.replace(/\s{2,}/g, " ").trim();
5817
5892
  return `className="${cleaned}"`;
@@ -6662,6 +6737,14 @@ function extractStyleContext(pageCode) {
6662
6737
  return `STYLE CONTEXT (match these patterns exactly for visual consistency with the Home page):
6663
6738
  ${lines.map((l) => ` - ${l}`).join("\n")}`;
6664
6739
  }
6740
+ var VALID_NAV_TYPES = /* @__PURE__ */ new Set(["header", "sidebar", "both"]);
6741
+ function parseNavTypeFromPlan(planResult) {
6742
+ const nav = planResult.navigation;
6743
+ if (nav && typeof nav.type === "string" && VALID_NAV_TYPES.has(nav.type)) {
6744
+ return nav.type;
6745
+ }
6746
+ return "header";
6747
+ }
6665
6748
  async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts) {
6666
6749
  let pageNames = [];
6667
6750
  spinner.start("Phase 1/4 \u2014 Planning pages...");
@@ -6675,6 +6758,10 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
6675
6758
  const route = c.route || `/${id}`;
6676
6759
  return { name, id, route };
6677
6760
  });
6761
+ const detectedNavType = parseNavTypeFromPlan(planResult);
6762
+ if (detectedNavType !== "header" && modCtx.config.navigation) {
6763
+ modCtx.config.navigation.type = detectedNavType;
6764
+ }
6678
6765
  } catch {
6679
6766
  spinner.text = "AI plan failed \u2014 extracting pages from your request...";
6680
6767
  }
@@ -7796,10 +7883,13 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7796
7883
  let codeToWrite = fixedCode;
7797
7884
  const { code: autoFixed, fixes: autoFixes } = await autoFixCode(codeToWrite);
7798
7885
  codeToWrite = autoFixed;
7799
- const { code: spaFixed, fixed: spaWasFixed } = detectAndFixSpaHomePage(codeToWrite, route);
7800
- if (spaWasFixed) {
7801
- codeToWrite = spaFixed;
7802
- autoFixes.push("replaced SPA-style home page with redirect to /dashboard");
7886
+ const hasDashboardPage = dsm.getConfig().pages.some((p) => p.route === "/dashboard");
7887
+ if (!hasDashboardPage) {
7888
+ const { code: spaFixed, fixed: spaWasFixed } = detectAndFixSpaHomePage(codeToWrite, route);
7889
+ if (spaWasFixed) {
7890
+ codeToWrite = spaFixed;
7891
+ autoFixes.push("replaced SPA-style home page with redirect to /dashboard");
7892
+ }
7803
7893
  }
7804
7894
  const { code: layoutStripped, stripped } = stripInlineLayoutElements(codeToWrite);
7805
7895
  codeToWrite = layoutStripped;
@@ -7839,7 +7929,7 @@ async function applyModification(request, dsm, cm, pm, projectRoot, aiProvider,
7839
7929
  layoutShared: manifestForAudit.shared.filter((c) => c.type === "layout"),
7840
7930
  allShared: manifestForAudit.shared
7841
7931
  });
7842
- const issues = validatePageQuality(codeToWrite);
7932
+ let issues = validatePageQuality(codeToWrite);
7843
7933
  const errors = issues.filter((i) => i.severity === "error");
7844
7934
  if (errors.length >= 2 && aiProvider) {
7845
7935
  console.log(
@@ -7864,8 +7954,15 @@ Rules:
7864
7954
  const recheckErrors = recheck.filter((i) => i.severity === "error");
7865
7955
  if (recheckErrors.length < errors.length) {
7866
7956
  codeToWrite = fixedCode2;
7957
+ const { code: reFixed, fixes: reFixes } = await autoFixCode(codeToWrite);
7958
+ if (reFixes.length > 0) {
7959
+ codeToWrite = reFixed;
7960
+ postFixes.push(...reFixes);
7961
+ }
7867
7962
  await writeFile(filePath, codeToWrite);
7868
- console.log(chalk11.green(` \u2714 Quality fix: ${errors.length} \u2192 ${recheckErrors.length} errors`));
7963
+ issues = validatePageQuality(codeToWrite);
7964
+ const finalErrors = issues.filter((i) => i.severity === "error").length;
7965
+ console.log(chalk11.green(` \u2714 Quality fix: ${errors.length} \u2192 ${finalErrors} errors`));
7869
7966
  }
7870
7967
  }
7871
7968
  }
@@ -7986,10 +8083,13 @@ ${pagesCtx}`
7986
8083
  let codeToWrite = fixedCode;
7987
8084
  const { code: autoFixed, fixes: autoFixes } = await autoFixCode(codeToWrite);
7988
8085
  codeToWrite = autoFixed;
7989
- const { code: spaFixed, fixed: spaWasFixed } = detectAndFixSpaHomePage(codeToWrite, route);
7990
- if (spaWasFixed) {
7991
- codeToWrite = spaFixed;
7992
- autoFixes.push("replaced SPA-style home page with redirect to /dashboard");
8086
+ const hasDashboardPage = dsm.getConfig().pages.some((p) => p.route === "/dashboard");
8087
+ if (!hasDashboardPage) {
8088
+ const { code: spaFixed, fixed: spaWasFixed } = detectAndFixSpaHomePage(codeToWrite, route);
8089
+ if (spaWasFixed) {
8090
+ codeToWrite = spaFixed;
8091
+ autoFixes.push("replaced SPA-style home page with redirect to /dashboard");
8092
+ }
7993
8093
  }
7994
8094
  const { code: layoutStripped, stripped } = stripInlineLayoutElements(codeToWrite);
7995
8095
  codeToWrite = layoutStripped;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.5.4",
6
+ "version": "0.5.5",
7
7
  "description": "CLI interface for Coherent Design Method",
8
8
  "type": "module",
9
9
  "main": "./dist/index.js",
@@ -33,8 +33,15 @@
33
33
  ],
34
34
  "author": "Coherent Design Method",
35
35
  "license": "MIT",
36
+ "scripts": {
37
+ "dev": "tsup --watch",
38
+ "build": "tsup",
39
+ "typecheck": "tsc --noEmit",
40
+ "test": "vitest"
41
+ },
36
42
  "dependencies": {
37
43
  "@anthropic-ai/sdk": "^0.32.0",
44
+ "@getcoherent/core": "workspace:*",
38
45
  "chalk": "^5.3.0",
39
46
  "chokidar": "^4.0.1",
40
47
  "commander": "^11.1.0",
@@ -42,19 +49,12 @@
42
49
  "open": "^10.1.0",
43
50
  "ora": "^7.0.1",
44
51
  "prompts": "^2.4.2",
45
- "zod": "^3.22.4",
46
- "@getcoherent/core": "0.5.4"
52
+ "zod": "^3.22.4"
47
53
  },
48
54
  "devDependencies": {
49
55
  "@types/node": "^20.11.0",
50
56
  "@types/prompts": "^2.4.9",
51
57
  "tsup": "^8.0.1",
52
58
  "typescript": "^5.3.3"
53
- },
54
- "scripts": {
55
- "dev": "tsup --watch",
56
- "build": "tsup",
57
- "typecheck": "tsc --noEmit",
58
- "test": "vitest"
59
59
  }
60
- }
60
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Sergei Kovtun
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.