@getcoherent/cli 0.6.1 → 0.6.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
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.
@@ -3,29 +3,83 @@ import { z } from "zod";
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
4
4
  import { dirname, resolve } from "path";
5
5
  import { mkdir, writeFile } from "fs/promises";
6
+ import chalk from "chalk";
7
+ var LAYOUT_SYNONYMS = {
8
+ horizontal: "header",
9
+ top: "header",
10
+ nav: "header",
11
+ navbar: "header",
12
+ topbar: "header",
13
+ "top-bar": "header",
14
+ vertical: "sidebar",
15
+ left: "sidebar",
16
+ side: "sidebar",
17
+ drawer: "sidebar",
18
+ full: "both",
19
+ combined: "both",
20
+ empty: "none",
21
+ minimal: "none",
22
+ clean: "none"
23
+ };
24
+ var PAGE_TYPE_SYNONYMS = {
25
+ landing: "marketing",
26
+ public: "marketing",
27
+ home: "marketing",
28
+ website: "marketing",
29
+ static: "marketing",
30
+ application: "app",
31
+ dashboard: "app",
32
+ admin: "app",
33
+ panel: "app",
34
+ console: "app",
35
+ authentication: "auth",
36
+ login: "auth",
37
+ "log-in": "auth",
38
+ register: "auth",
39
+ signin: "auth",
40
+ "sign-in": "auth",
41
+ signup: "auth",
42
+ "sign-up": "auth"
43
+ };
44
+ var COMPONENT_TYPE_SYNONYMS = {
45
+ component: "widget",
46
+ ui: "widget",
47
+ element: "widget",
48
+ block: "widget",
49
+ "page-section": "section",
50
+ hero: "section",
51
+ feature: "section",
52
+ area: "section"
53
+ };
54
+ function normalizeEnum(synonyms) {
55
+ return (v) => {
56
+ const trimmed = v.trim().toLowerCase();
57
+ return synonyms[trimmed] ?? trimmed;
58
+ };
59
+ }
6
60
  var RouteGroupSchema = z.object({
7
61
  id: z.string(),
8
- layout: z.enum(["header", "sidebar", "both", "none"]),
62
+ layout: z.string().transform(normalizeEnum(LAYOUT_SYNONYMS)).pipe(z.enum(["header", "sidebar", "both", "none"])),
9
63
  pages: z.array(z.string())
10
64
  });
11
65
  var PlannedComponentSchema = z.object({
12
66
  name: z.string(),
13
- description: z.string(),
14
- props: z.string(),
15
- usedBy: z.array(z.string()),
16
- type: z.enum(["section", "widget"]),
67
+ description: z.string().default(""),
68
+ props: z.string().default("{}"),
69
+ usedBy: z.array(z.string()).default([]),
70
+ type: z.string().transform(normalizeEnum(COMPONENT_TYPE_SYNONYMS)).pipe(z.enum(["section", "widget"])),
17
71
  shadcnDeps: z.array(z.string()).default([])
18
72
  });
19
73
  var PageNoteSchema = z.object({
20
- type: z.enum(["marketing", "app", "auth"]),
21
- sections: z.array(z.string()),
74
+ type: z.string().transform(normalizeEnum(PAGE_TYPE_SYNONYMS)).pipe(z.enum(["marketing", "app", "auth"])),
75
+ sections: z.array(z.string()).default([]),
22
76
  links: z.record(z.string()).optional()
23
77
  });
24
78
  var ArchitecturePlanSchema = z.object({
25
79
  appName: z.string().optional(),
26
80
  groups: z.array(RouteGroupSchema),
27
- sharedComponents: z.array(PlannedComponentSchema).max(8),
28
- pageNotes: z.record(z.string(), PageNoteSchema)
81
+ sharedComponents: z.array(PlannedComponentSchema).max(8).default([]),
82
+ pageNotes: z.record(z.string(), PageNoteSchema).default({})
29
83
  });
30
84
  function routeToKey(route) {
31
85
  return route.replace(/^\//, "") || "home";
@@ -59,16 +113,21 @@ async function generateArchitecturePlan(pages, userMessage, aiProvider, layoutHi
59
113
  User's request: "${userMessage}"
60
114
 
61
115
  Navigation type requested: ${layoutHint || "auto-detect"}`;
116
+ const warnings = [];
62
117
  for (let attempt = 0; attempt < 2; attempt++) {
63
118
  try {
64
119
  const raw = await aiProvider.generateJSON(PLAN_SYSTEM_PROMPT, userPrompt);
65
120
  const parsed = ArchitecturePlanSchema.safeParse(raw);
66
- if (parsed.success) return parsed.data;
67
- } catch {
68
- if (attempt === 1) return null;
121
+ if (parsed.success) return { plan: parsed.data, warnings };
122
+ warnings.push(
123
+ `Validation (attempt ${attempt + 1}): ${parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
124
+ );
125
+ } catch (err) {
126
+ warnings.push(`Error (attempt ${attempt + 1}): ${err instanceof Error ? err.message : String(err)}`);
127
+ if (attempt === 1) return { plan: null, warnings };
69
128
  }
70
129
  }
71
- return null;
130
+ return { plan: null, warnings };
72
131
  }
73
132
  async function updateArchitecturePlan(existingPlan, newPages, userMessage, aiProvider) {
74
133
  const userPrompt = `Existing plan:
@@ -83,7 +142,10 @@ Update the existing plan to include these new pages. Keep all existing groups, c
83
142
  const raw = await aiProvider.generateJSON(PLAN_SYSTEM_PROMPT, userPrompt);
84
143
  const parsed = ArchitecturePlanSchema.safeParse(raw);
85
144
  if (parsed.success) return parsed.data;
86
- } catch {
145
+ const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
146
+ console.warn(chalk.dim(` Plan update validation failed: ${issues}`));
147
+ } catch (err) {
148
+ console.warn(chalk.dim(` Plan update error: ${err instanceof Error ? err.message : String(err)}`));
87
149
  }
88
150
  const merged = structuredClone(existingPlan);
89
151
  const largestGroup = merged.groups.reduce(
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  loadPlan,
6
6
  routeToKey,
7
7
  savePlan
8
- } from "./chunk-INW3BQSX.js";
8
+ } from "./chunk-IKHAW6OI.js";
9
9
  import {
10
10
  __require
11
11
  } from "./chunk-3RG5ZIWI.js";
@@ -7191,7 +7191,8 @@ async function warnInlineDuplicates(projectRoot, pageName, route, pageCode, mani
7191
7191
  for (const t of sharedTokens) {
7192
7192
  if (pageTokens.includes(t)) overlap++;
7193
7193
  }
7194
- if (overlap >= 12 && sharedTokens.size >= 10) {
7194
+ const overlapRatio = sharedTokens.size > 0 ? overlap / sharedTokens.size : 0;
7195
+ if (overlap >= 20 && overlapRatio >= 0.6) {
7195
7196
  console.log(
7196
7197
  chalk8.yellow(
7197
7198
  `
@@ -7963,7 +7964,13 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
7963
7964
  try {
7964
7965
  const ai = await createAIProvider(provider ?? "auto");
7965
7966
  const layoutHint = modCtx.config.navigation?.type || null;
7966
- plan = await generateArchitecturePlan(pageNames, message, ai, layoutHint);
7967
+ const { plan: generatedPlan, warnings: planWarnings } = await generateArchitecturePlan(
7968
+ pageNames,
7969
+ message,
7970
+ ai,
7971
+ layoutHint
7972
+ );
7973
+ plan = generatedPlan;
7967
7974
  if (plan) {
7968
7975
  const groupsSummary = plan.groups.map((g) => `${g.id} (${g.layout}, ${g.pages.length} pages)`).join(", ");
7969
7976
  const sharedSummary = plan.sharedComponents.length > 0 ? plan.sharedComponents.map((c) => `${c.name} \u2192 ${c.usedBy.join(", ")}`).join(" | ") : "";
@@ -7987,6 +7994,9 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
7987
7994
  } else {
7988
7995
  spinner.warn("Phase 2/6 \u2014 Plan generation failed (continuing without plan)");
7989
7996
  }
7997
+ for (const w of planWarnings) {
7998
+ console.log(chalk9.dim(` ${w}`));
7999
+ }
7990
8000
  } catch {
7991
8001
  spinner.warn("Phase 2/6 \u2014 Plan generation failed (continuing without plan)");
7992
8002
  }
@@ -8046,7 +8056,7 @@ async function splitGeneratePages(spinner, message, modCtx, provider, parseOpts)
8046
8056
  if (plan && plan.sharedComponents.length > 0) {
8047
8057
  spinner.start(`Phase 4.5/6 \u2014 Generating ${plan.sharedComponents.length} shared components from plan...`);
8048
8058
  try {
8049
- const { generateSharedComponentsFromPlan } = await import("./plan-generator-IS3YDZUW.js");
8059
+ const { generateSharedComponentsFromPlan } = await import("./plan-generator-R72I6RNM.js");
8050
8060
  const generated = await generateSharedComponentsFromPlan(
8051
8061
  plan,
8052
8062
  styleContext,
@@ -10456,8 +10466,16 @@ async function chatCommand(message, options) {
10456
10466
  if (authRelated) authRelated.forEach((l) => allLinkedRoutes.add(l));
10457
10467
  }
10458
10468
  const existingRoutes = new Set(currentConfig.pages.map((p) => p.route).filter(Boolean));
10469
+ const expandedExisting = new Set(existingRoutes);
10470
+ for (const route of existingRoutes) {
10471
+ const canonical = AUTH_SYNONYMS[route] ?? route;
10472
+ expandedExisting.add(canonical);
10473
+ for (const [syn, can] of Object.entries(AUTH_SYNONYMS)) {
10474
+ if (can === canonical) expandedExisting.add(syn);
10475
+ }
10476
+ }
10459
10477
  const missingRoutes = [...allLinkedRoutes].filter((route) => {
10460
- if (existingRoutes.has(route)) return false;
10478
+ if (expandedExisting.has(route)) return false;
10461
10479
  if (existsSync16(routeToFsPath(projectRoot, route, false))) return false;
10462
10480
  if (existsSync16(routeToFsPath(projectRoot, route, true))) return false;
10463
10481
  return true;
@@ -10670,8 +10688,9 @@ async function chatCommand(message, options) {
10670
10688
  const preflightNames = preflightInstalledIds.map((id) => updatedConfig.components.find((c) => c.id === id)?.name).filter(Boolean);
10671
10689
  showPreview(normalizedRequests, results, updatedConfig, preflightNames);
10672
10690
  if (scaffoldedPages.length > 0) {
10691
+ const uniqueScaffolded = [...new Map(scaffoldedPages.map((s) => [s.route, s])).values()];
10673
10692
  console.log(chalk14.cyan("\u{1F517} Auto-scaffolded linked pages:"));
10674
- scaffoldedPages.forEach(({ route, name }) => {
10693
+ uniqueScaffolded.forEach(({ route, name }) => {
10675
10694
  console.log(chalk14.white(` \u2728 ${name} \u2192 ${route}`));
10676
10695
  });
10677
10696
  console.log("");
@@ -11604,7 +11623,7 @@ async function previewCommand() {
11604
11623
  }
11605
11624
  console.log(chalk15.green("\n\u2705 Dependencies installed\n"));
11606
11625
  } else {
11607
- spinner.succeed("Project ready");
11626
+ spinner.succeed("Dependencies installed");
11608
11627
  }
11609
11628
  if (needsGlobalsFix(projectRoot)) {
11610
11629
  spinner.text = "Fixing globals.css...";
@@ -11,7 +11,7 @@ import {
11
11
  routeToKey,
12
12
  savePlan,
13
13
  updateArchitecturePlan
14
- } from "./chunk-INW3BQSX.js";
14
+ } from "./chunk-IKHAW6OI.js";
15
15
  import "./chunk-3RG5ZIWI.js";
16
16
  export {
17
17
  ArchitecturePlanSchema,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.6.1",
6
+ "version": "0.6.3",
7
7
  "description": "CLI interface for Coherent Design Method",
8
8
  "type": "module",
9
9
  "main": "./dist/index.js",
@@ -33,15 +33,8 @@
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
- },
42
36
  "dependencies": {
43
37
  "@anthropic-ai/sdk": "^0.32.0",
44
- "@getcoherent/core": "workspace:*",
45
38
  "chalk": "^5.3.0",
46
39
  "chokidar": "^4.0.1",
47
40
  "commander": "^11.1.0",
@@ -49,12 +42,19 @@
49
42
  "open": "^10.1.0",
50
43
  "ora": "^7.0.1",
51
44
  "prompts": "^2.4.2",
52
- "zod": "^3.22.4"
45
+ "zod": "^3.22.4",
46
+ "@getcoherent/core": "0.6.3"
53
47
  },
54
48
  "devDependencies": {
55
49
  "@types/node": "^20.11.0",
56
50
  "@types/prompts": "^2.4.9",
57
51
  "tsup": "^8.0.1",
58
52
  "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
+ }