@decantr/cli 1.6.1 → 1.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/dist/bin.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-DESYSWLL.js";
3
- import "./chunk-PMBLHVBX.js";
2
+ import "./chunk-HWMN432I.js";
3
+ import "./chunk-5MKMLONH.js";
@@ -217,20 +217,13 @@ function generateTreatmentCSS(spatialTokens, treatmentOverrides, themeDecorators
217
217
  lines.push("}");
218
218
  lines.push("");
219
219
  lines.push("} /* end @layer treatments */");
220
- if (themeDecorators && Object.keys(themeDecorators).length > 0) {
221
- const label = themeName ? ` (${themeName})` : "";
222
- lines.push("");
223
- lines.push("@layer decorators {");
224
- lines.push("");
225
- lines.push(`/* \u2500\u2500 Layer 3: Theme Decorators${label} \u2500\u2500 */`);
226
- lines.push("");
227
- for (const [name, description] of Object.entries(themeDecorators)) {
228
- const rule = generateDecoratorRule(name, description);
229
- lines.push(rule);
230
- lines.push("");
231
- }
232
- lines.push("} /* end @layer decorators */");
233
- }
220
+ const decoratorBlock = `
221
+ @layer decorators {
222
+ /* Decorator CSS is AI-generated from structured definitions in section context files. */
223
+ /* See .decantr/context/section-*.md for intent, suggested properties, and usage guidance. */
224
+ }
225
+ `;
226
+ lines.push(decoratorBlock);
234
227
  return lines.join("\n");
235
228
  }
236
229
  function generatePersonalityCSS(personality, themeData) {
@@ -252,6 +245,7 @@ function generatePersonalityCSS(personality, themeData) {
252
245
  rules.push(`.status-ring[data-status="error"] { border-color: var(--d-error); box-shadow: 0 0 12px color-mix(in srgb, var(--d-error) 25%, transparent); }`);
253
246
  rules.push(`.status-ring[data-status="warning"] { border-color: var(--d-warning); }`);
254
247
  rules.push(`.status-ring[data-status="idle"] { border-color: var(--d-text-muted); }`);
248
+ rules.push(`.status-ring[data-status="processing"] { border-color: var(--d-primary); }`);
255
249
  rules.push(`@keyframes pulse-ring { 0% { opacity: 0.6; transform: scale(1); } 100% { opacity: 0; transform: scale(1.3); } }`);
256
250
  rules.push(`.status-ring[data-status="active"]::after { content: ''; position: absolute; inset: -4px; border-radius: 50%; border: 2px solid var(--d-success); opacity: 0; animation: pulse-ring 2s ease-out infinite; }`);
257
251
  }
@@ -539,7 +533,7 @@ function generateTokensCSS(themeData, mode, spatialTokens) {
539
533
  return {
540
534
  // Seed colors
541
535
  "--d-primary": seed.primary || "#6366f1",
542
- "--d-secondary": seed.secondary || "#a1a1aa",
536
+ "--d-secondary": palette.secondary?.[tokenMode] || palette.secondary?.dark || seed.secondary || "#A1A1AA",
543
537
  "--d-accent": seed.accent || "#f59e0b",
544
538
  // Palette colors (mode-aware)
545
539
  "--d-bg": palette.background?.[tokenMode] || "#18181b",
@@ -693,135 +687,6 @@ input, button, textarea, select {
693
687
  }
694
688
  `;
695
689
  }
696
- function generateDecoratorRule(name, description) {
697
- const rules = [];
698
- const descLower = description.toLowerCase();
699
- const nameLower = name.toLowerCase();
700
- const isCard = descLower.includes("card") || descLower.includes("panel") || nameLower.includes("card") || nameLower.includes("panel");
701
- const isInput = descLower.includes("input") || descLower.includes("field") || descLower.includes("textarea") || nameLower.includes("input") || nameLower.includes("textarea");
702
- const isGlass = descLower.includes("glassmorphic") || descLower.includes("glass") || nameLower.includes("glass");
703
- const isInteractive = isCard || isInput || isGlass;
704
- const isNonInteractive = descLower.includes("divider") || descLower.includes("skeleton") || descLower.includes("keyframe") || descLower.includes("canvas") || nameLower.includes("divider") || nameLower.includes("skeleton") || nameLower.includes("canvas");
705
- if (descLower.includes("monospace") || descLower.includes("mono font")) {
706
- rules.push("font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace");
707
- }
708
- if (descLower.includes("surface-raised") || descLower.includes("surface raised")) {
709
- rules.push("background: var(--d-surface-raised)");
710
- } else if (descLower.includes("surface background") || descLower.includes("surface elevation")) {
711
- rules.push("background: var(--d-surface)");
712
- } else if (descLower.includes("background") && descLower.includes("theme")) {
713
- rules.push("background: var(--d-bg)");
714
- } else if (descLower.includes("primary-tinted") || descLower.includes("primary background")) {
715
- rules.push("background: color-mix(in srgb, var(--d-primary) 15%, var(--d-surface))");
716
- }
717
- if (isInput) {
718
- if (!rules.some((r) => r.startsWith("background"))) {
719
- rules.push("background: var(--d-surface)");
720
- }
721
- rules.push("color: var(--d-text)");
722
- rules.push("padding: 0.5rem 0.75rem");
723
- rules.push("border-radius: var(--d-radius)");
724
- rules.push("width: 100%");
725
- rules.push("outline: none");
726
- }
727
- const leftBorderMatch = descLower.match(/(\d+)px\s+left\s+border/);
728
- if (leftBorderMatch) {
729
- rules.push(`border-left: ${leftBorderMatch[1]}px solid var(--d-primary)`);
730
- } else if (descLower.includes("left border")) {
731
- rules.push("border-left: 3px solid var(--d-primary)");
732
- } else if (descLower.includes("1px border") || descLower.includes("subtle border")) {
733
- rules.push("border: 1px solid var(--d-border)");
734
- } else if (descLower.includes("border") && !descLower.includes("radius")) {
735
- rules.push("border: 1px solid var(--d-border)");
736
- }
737
- const radiusMatch = descLower.match(/(\d+)px radius/);
738
- if (radiusMatch) {
739
- rules.push(`border-radius: ${radiusMatch[1]}px`);
740
- } else if ((descLower.includes("radius") || descLower.includes("rounded")) && !isInput) {
741
- rules.push("border-radius: var(--d-radius)");
742
- }
743
- if (descLower.includes("elevation") || descLower.includes("shadow")) {
744
- rules.push("box-shadow: var(--d-shadow)");
745
- }
746
- if (isInteractive && !isNonInteractive) {
747
- rules.push("transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease");
748
- }
749
- if (descLower.includes("entrance animation") || descLower.includes("fade")) {
750
- rules.push("animation: decantr-fade-in 0.2s ease-out");
751
- }
752
- if (descLower.includes("pulse animation") || descLower.includes("skeleton")) {
753
- rules.push("animation: decantr-pulse 1.5s ease-in-out infinite");
754
- }
755
- const blurMatch = descLower.match(/blur\((\d+)px\)/);
756
- if (blurMatch) {
757
- rules.push(`backdrop-filter: blur(${blurMatch[1]}px)`);
758
- rules.push(`-webkit-backdrop-filter: blur(${blurMatch[1]}px)`);
759
- } else if (isGlass) {
760
- rules.push("backdrop-filter: blur(12px)");
761
- rules.push("-webkit-backdrop-filter: blur(12px)");
762
- } else if (descLower.includes("blur")) {
763
- rules.push("backdrop-filter: blur(8px)");
764
- rules.push("-webkit-backdrop-filter: blur(8px)");
765
- }
766
- if (descLower.includes("semi-transparent") || descLower.includes("glassmorphic")) {
767
- const bgIdx = rules.findIndex((r) => r.startsWith("background:"));
768
- const rgbaBg = "background: rgba(31, 31, 35, 0.8)";
769
- if (bgIdx !== -1) {
770
- rules[bgIdx] = rgbaBg;
771
- } else {
772
- rules.push(rgbaBg);
773
- }
774
- }
775
- if (descLower.includes("right-aligned")) {
776
- rules.push("margin-left: auto");
777
- } else if (descLower.includes("left-aligned")) {
778
- rules.push("margin-right: auto");
779
- }
780
- if (descLower.includes("message bubble") || descLower.includes("bubble")) {
781
- rules.push("padding: var(--d-gap-3) var(--d-gap-4)");
782
- rules.push("border-radius: var(--d-radius-lg)");
783
- rules.push("max-width: 80%");
784
- }
785
- if (descLower.includes("monospace") || descLower.includes("code")) {
786
- if (!rules.some((r) => r.startsWith("padding"))) {
787
- rules.push("padding: 0.75rem 1rem");
788
- }
789
- if (!rules.some((r) => r.startsWith("border-radius"))) {
790
- rules.push("border-radius: var(--d-radius-sm)");
791
- }
792
- rules.push("overflow-x: auto");
793
- }
794
- if (rules.length === 0) {
795
- return `/* .${name}: ${description} */`;
796
- }
797
- let css = `.${name} {
798
- ${rules.join(";\n ")};
799
- }`;
800
- if (isInteractive && !isNonInteractive) {
801
- const stateRules = [];
802
- if (isCard || isGlass) {
803
- stateRules.push(`.${name}:hover {
804
- border-color: var(--d-primary-hover, var(--d-border));
805
- box-shadow: var(--d-shadow-md);
806
- }`);
807
- }
808
- if (isInput) {
809
- stateRules.push(`.${name}:focus {
810
- border-color: var(--d-primary);
811
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--d-primary) 25%, transparent);
812
- }`);
813
- stateRules.push(`.${name}::placeholder {
814
- color: var(--d-text-muted);
815
- }`);
816
- stateRules.push(`.${name}:disabled {
817
- opacity: 0.5;
818
- cursor: not-allowed;
819
- }`);
820
- }
821
- css += "\n\n" + stateRules.join("\n\n");
822
- }
823
- return css;
824
- }
825
690
  function serializeLayoutItem(item) {
826
691
  if (typeof item === "string") {
827
692
  return item;
@@ -897,64 +762,6 @@ function resolvePatternAlias(item, patterns) {
897
762
  }
898
763
  return item;
899
764
  }
900
- function buildEssence(options, archetypeData) {
901
- let structure = [
902
- { id: "home", shell: options.shell, layout: ["hero"] }
903
- ];
904
- let features = options.features;
905
- if (archetypeData?.pages) {
906
- structure = archetypeData.pages.map((p) => {
907
- const resolvedLayout = (p.default_layout?.length ? p.default_layout : ["hero"]).map((item) => resolvePatternAlias(item, p.patterns));
908
- return {
909
- id: p.id,
910
- shell: p.shell || options.shell,
911
- layout: resolvedLayout
912
- };
913
- });
914
- }
915
- if (archetypeData?.features) {
916
- features = [.../* @__PURE__ */ new Set([...features, ...archetypeData.features])];
917
- }
918
- const contentGapMap = {
919
- compact: "_gap2",
920
- comfortable: "_gap4",
921
- spacious: "_gap6"
922
- };
923
- const archetype = options.archetype || "custom";
924
- const essence = {
925
- version: "2.0.0",
926
- archetype,
927
- theme: {
928
- style: options.theme,
929
- mode: options.mode,
930
- recipe: options.theme,
931
- // Legacy v2 field — kept for backward compatibility
932
- shape: options.shape
933
- },
934
- personality: options.personality,
935
- platform: {
936
- type: "spa",
937
- routing: "hash"
938
- },
939
- structure,
940
- features,
941
- guard: {
942
- enforce_style: true,
943
- enforce_recipe: true,
944
- // Legacy v2 field — kept for backward compatibility
945
- mode: options.guard
946
- },
947
- density: {
948
- level: options.density,
949
- content_gap: contentGapMap[options.density] || "_gap4"
950
- },
951
- target: options.target
952
- };
953
- if (options.accessibility) {
954
- essence.accessibility = options.accessibility;
955
- }
956
- return essence;
957
- }
958
765
  function buildEssenceV3(options, archetypeData, themeHints) {
959
766
  let pages = [
960
767
  { id: "home", layout: ["hero"] }
@@ -1321,10 +1128,8 @@ function generateDecantrMdV31(params) {
1321
1128
  briefLines.push("## Project Brief");
1322
1129
  briefLines.push("");
1323
1130
  briefLines.push(`- **Blueprint:** ${params.blueprintId || "custom"}`);
1324
- const themeParts = [params.themeName || "default"];
1325
- if (params.themeMode) themeParts.push(`${params.themeMode} mode`);
1326
- if (params.themeShape) themeParts.push(params.themeShape);
1327
- briefLines.push(`- **Theme:** ${themeParts.join(" (").replace(/ \($/, "") + (themeParts.length > 1 ? ")" : "")}`);
1131
+ const themeDesc = `${params.themeName || "default"} (${params.themeMode || "dark"} mode${params.themeShape ? `, ${params.themeShape} shape` : ""})`;
1132
+ briefLines.push(`- **Theme:** ${themeDesc}`);
1328
1133
  if (params.personality && params.personality.length > 0) {
1329
1134
  briefLines.push(`- **Personality:** ${params.personality.join(". ")}`);
1330
1135
  }
@@ -1337,7 +1142,17 @@ function generateDecantrMdV31(params) {
1337
1142
  }
1338
1143
  briefLines.push(`- **Guard mode:** ${params.guardMode}`);
1339
1144
  briefLines.push("");
1340
- if (params.decorators && params.decorators.length > 0) {
1145
+ if (params.decoratorDefinitions && Object.keys(params.decoratorDefinitions).length > 0) {
1146
+ briefLines.push("### Decorator Quick Reference");
1147
+ briefLines.push("| Class | Intent | Key CSS |");
1148
+ briefLines.push("|-------|--------|---------|");
1149
+ for (const [name, def] of Object.entries(params.decoratorDefinitions)) {
1150
+ const intent = def.intent || "";
1151
+ const cssProps = def.css ? Object.entries(def.css).map(([p, v]) => `${p}: ${v}`).join("; ") : "";
1152
+ briefLines.push(`| \`.${name}\` | ${intent} | ${cssProps} |`);
1153
+ }
1154
+ briefLines.push("");
1155
+ } else if (params.decorators && params.decorators.length > 0) {
1341
1156
  briefLines.push("### Decorator Quick Reference");
1342
1157
  briefLines.push("| Class | Purpose |");
1343
1158
  briefLines.push("|-------|---------|");
@@ -1381,6 +1196,9 @@ function generateProjectJson(detected, options, registrySource) {
1381
1196
  flags: buildFlagsString(options)
1382
1197
  }
1383
1198
  };
1199
+ if (options.blueprint) {
1200
+ data.blueprintId = options.blueprint;
1201
+ }
1384
1202
  return JSON.stringify(data, null, 2);
1385
1203
  }
1386
1204
  function buildFlagsString(options) {
@@ -1391,38 +1209,23 @@ function buildFlagsString(options) {
1391
1209
  if (options.guard) flags.push(`--guard=${options.guard}`);
1392
1210
  return flags.join(" ");
1393
1211
  }
1394
- function generateTaskContext(templateName, essence) {
1395
- const template = loadTemplate(templateName);
1396
- const defaultShell = essence.structure[0]?.shell || "sidebar-main";
1397
- const layout = essence.structure[0]?.layout.map(serializeLayoutItem).join(", ") || "none";
1398
- const scaffoldStructure = essence.structure.map((p) => {
1399
- const patterns = p.layout.length > 0 ? `
1400
- - Patterns: ${p.layout.map(serializeLayoutItem).join(", ")}` : "";
1401
- return `- **${p.id}** (${p.shell})${patterns}`;
1402
- }).join("\n");
1403
- const vars = {
1404
- TARGET: essence.target,
1405
- THEME_STYLE: essence.theme.style,
1406
- THEME_MODE: essence.theme.mode,
1407
- THEME_RECIPE: essence.theme.style,
1408
- DEFAULT_SHELL: defaultShell,
1409
- GUARD_MODE: essence.guard.mode,
1410
- LAYOUT: layout,
1411
- DENSITY: essence.density.level,
1412
- CONTENT_GAP: essence.density.content_gap,
1413
- SCAFFOLD_STRUCTURE: scaffoldStructure
1414
- };
1415
- return renderTemplate(template, vars);
1416
- }
1417
1212
  function generateTaskContextV3(templateName, essence) {
1418
1213
  const template = loadTemplate(templateName);
1419
- const pages = essence.blueprint.sections && essence.blueprint.sections.length > 0 ? essence.blueprint.sections.flatMap((s) => s.pages) : essence.blueprint.pages || [];
1420
- const defaultShell = essence.blueprint.sections?.[0]?.shell || essence.blueprint.shell || "sidebar-main";
1214
+ const sections = essence.blueprint.sections && essence.blueprint.sections.length > 0 ? essence.blueprint.sections : [];
1215
+ const pages = sections.length > 0 ? sections.flatMap((s) => s.pages) : essence.blueprint.pages || [];
1216
+ const defaultShell = sections[0]?.shell || essence.blueprint.shell || "sidebar-main";
1421
1217
  const layout = pages[0]?.layout?.map(serializeLayoutItem).join(", ") || "none";
1218
+ const pageShellMap = /* @__PURE__ */ new Map();
1219
+ for (const s of sections) {
1220
+ for (const p of s.pages) {
1221
+ pageShellMap.set(p.id, s.shell);
1222
+ }
1223
+ }
1422
1224
  const scaffoldStructure = pages.map((p) => {
1225
+ const shell = pageShellMap.get(p.id) || defaultShell;
1423
1226
  const patterns = p.layout.length > 0 ? `
1424
1227
  - Patterns: ${p.layout.map(serializeLayoutItem).join(", ")}` : "";
1425
- return `- **${p.id}** (${defaultShell})${patterns}`;
1228
+ return `- **${p.id}** (${shell})${patterns}`;
1426
1229
  }).join("\n");
1427
1230
  const densityLevel = essence.dna.spacing?.density || "comfortable";
1428
1231
  const contentGap = essence.dna.spacing?.content_gap || "_gap4";
@@ -1506,7 +1309,6 @@ ${cacheEntry}
1506
1309
  }
1507
1310
  async function scaffoldProject(projectRoot, options, detected, registry, archetypeData, registrySource = "cache", themeData, topologyMarkdown, composedSections, routeMap, patternSpecs, blueprintData) {
1508
1311
  const essenceV3 = buildEssenceV3(options, archetypeData, themeData);
1509
- const essence = buildEssence(options, archetypeData);
1510
1312
  const decantrDir = join(projectRoot, ".decantr");
1511
1313
  const contextDir = join(decantrDir, "context");
1512
1314
  const cacheDir = join(decantrDir, "cache");
@@ -1515,10 +1317,15 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
1515
1317
  const essencePath = join(projectRoot, "decantr.essence.json");
1516
1318
  writeFileSync(essencePath, JSON.stringify(essenceV3, null, 2) + "\n");
1517
1319
  const projectJsonPath = join(decantrDir, "project.json");
1518
- writeFileSync(projectJsonPath, generateProjectJson(detected, options, registrySource));
1320
+ const projectJsonStr = generateProjectJson(detected, options, registrySource);
1321
+ const projectJsonObj = JSON.parse(projectJsonStr);
1322
+ if (blueprintData?.voice) {
1323
+ projectJsonObj.voice = blueprintData.voice;
1324
+ }
1325
+ writeFileSync(projectJsonPath, JSON.stringify(projectJsonObj, null, 2));
1519
1326
  const contextFiles = [];
1520
1327
  const scaffoldPath = join(contextDir, "task-scaffold.md");
1521
- writeFileSync(scaffoldPath, generateTaskContext("task-scaffold.md.template", essence));
1328
+ writeFileSync(scaffoldPath, generateTaskContextV3("task-scaffold.md.template", essenceV3));
1522
1329
  contextFiles.push(scaffoldPath);
1523
1330
  if (composedSections) {
1524
1331
  essenceV3.version = "3.1.0";
@@ -1541,7 +1348,7 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
1541
1348
  }
1542
1349
  writeFileSync(essencePath, JSON.stringify(essenceV3, null, 2) + "\n");
1543
1350
  }
1544
- const refreshResult = await refreshDerivedFiles(projectRoot, essenceV3, registry, themeData, { isInitialScaffold: true });
1351
+ const refreshResult = await refreshDerivedFiles(projectRoot, essenceV3, registry, themeData, { isInitialScaffold: true, patternSpecs });
1545
1352
  contextFiles.push(...refreshResult.contextFiles);
1546
1353
  const gitignoreUpdated = updateGitignore(projectRoot);
1547
1354
  return {
@@ -1741,10 +1548,81 @@ When available, use these tools:
1741
1548
  gitignoreUpdated
1742
1549
  };
1743
1550
  }
1551
+ async function resolvePatternSpec(name, registry, prefetched, includeExtendedFields = true) {
1552
+ if (prefetched && (prefetched.layout_hints || prefetched.visual_brief || !includeExtendedFields)) {
1553
+ if (!prefetched.components || prefetched.components.length === 0) {
1554
+ const syntheticComps2 = generateSyntheticComponents(name, prefetched.description);
1555
+ if (syntheticComps2.length > 0) prefetched.components = syntheticComps2;
1556
+ }
1557
+ if (!prefetched.slots || Object.keys(prefetched.slots).length === 0) {
1558
+ const synthetic = generateSyntheticSlots(name, prefetched.description);
1559
+ if (Object.keys(synthetic).length > 0) prefetched.slots = synthetic;
1560
+ }
1561
+ return prefetched;
1562
+ }
1563
+ try {
1564
+ const patResult = await registry.fetchPattern(name);
1565
+ if (patResult?.data) {
1566
+ const inner = patResult.data;
1567
+ const defaultPreset = inner.default_preset || "standard";
1568
+ const preset = inner.presets?.[defaultPreset];
1569
+ let slots = preset?.layout?.slots || {};
1570
+ if (Object.keys(slots).length === 0) {
1571
+ const synthetic = generateSyntheticSlots(name, inner.description || "");
1572
+ if (Object.keys(synthetic).length > 0) slots = synthetic;
1573
+ }
1574
+ const spec = {
1575
+ description: inner.description || prefetched?.description || "",
1576
+ components: inner.components || prefetched?.components || [],
1577
+ slots,
1578
+ layout_hints: inner.layout_hints,
1579
+ ...includeExtendedFields ? {
1580
+ visual_brief: inner.visual_brief,
1581
+ composition: inner.composition,
1582
+ motion: inner.motion,
1583
+ responsive: inner.responsive,
1584
+ accessibility: inner.accessibility
1585
+ } : {}
1586
+ };
1587
+ if (!spec.components || spec.components.length === 0) {
1588
+ const syntheticComps2 = generateSyntheticComponents(name, spec.description);
1589
+ if (syntheticComps2.length > 0) spec.components = syntheticComps2;
1590
+ }
1591
+ return spec;
1592
+ }
1593
+ } catch {
1594
+ }
1595
+ if (prefetched) {
1596
+ if (!prefetched.components || prefetched.components.length === 0) {
1597
+ const syntheticComps2 = generateSyntheticComponents(name, prefetched.description);
1598
+ if (syntheticComps2.length > 0) prefetched.components = syntheticComps2;
1599
+ }
1600
+ if (!prefetched.slots || Object.keys(prefetched.slots).length === 0) {
1601
+ const synthetic = generateSyntheticSlots(name, prefetched.description);
1602
+ if (Object.keys(synthetic).length > 0) prefetched.slots = synthetic;
1603
+ }
1604
+ return prefetched;
1605
+ }
1606
+ const syntheticSlots = generateSyntheticSlots(name, "");
1607
+ const syntheticComps = generateSyntheticComponents(name, "");
1608
+ if (Object.keys(syntheticSlots).length > 0 || syntheticComps.length > 0) {
1609
+ return { description: "", components: syntheticComps, slots: syntheticSlots };
1610
+ }
1611
+ return null;
1612
+ }
1744
1613
  async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThemeData, options) {
1745
1614
  const decantrDir = join(projectRoot, ".decantr");
1746
1615
  const contextDir = join(decantrDir, "context");
1747
1616
  mkdirSync(contextDir, { recursive: true });
1617
+ let storedBlueprintId;
1618
+ const projectJsonFilePath = join(decantrDir, "project.json");
1619
+ if (existsSync(projectJsonFilePath)) {
1620
+ try {
1621
+ const projData = JSON.parse(readFileSync(projectJsonFilePath, "utf-8"));
1622
+ if (projData.blueprintId) storedBlueprintId = projData.blueprintId;
1623
+ } catch {
1624
+ }
1625
+ }
1748
1626
  const themeName = essence.dna.theme.id || essence.dna.theme.style || "default";
1749
1627
  const mode = essence.dna.theme.mode;
1750
1628
  const guardMode = essence.meta.guard.mode;
@@ -1862,14 +1740,15 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1862
1740
  writeFileSync(decantrMdPath, generateDecantrMdV31({
1863
1741
  guardMode,
1864
1742
  cssApproach: CSS_APPROACH_CONTENT,
1865
- blueprintId: essence.meta.blueprint || void 0,
1743
+ blueprintId: storedBlueprintId || essence.meta.blueprint || void 0,
1866
1744
  themeName,
1867
1745
  themeMode: mode,
1868
1746
  themeShape: essence.dna.theme.shape || void 0,
1869
1747
  personality,
1870
1748
  sections: sectionSummaries.length > 0 ? sectionSummaries : void 0,
1871
1749
  features: allFeatures.length > 0 ? allFeatures : void 0,
1872
- decorators: earlyDecoratorList.length > 0 ? earlyDecoratorList : void 0
1750
+ decorators: earlyDecoratorList.length > 0 ? earlyDecoratorList : void 0,
1751
+ decoratorDefinitions: themeData?.decorator_definitions
1873
1752
  }));
1874
1753
  const hasSections = essence.blueprint.sections && essence.blueprint.sections.length > 0;
1875
1754
  const contextFiles = [];
@@ -1895,6 +1774,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1895
1774
  section.shell = primarySectionShell;
1896
1775
  }
1897
1776
  }
1777
+ const prefetchedSpecs = options?.patternSpecs;
1898
1778
  const patternSpecs = {};
1899
1779
  const seenPatterns = /* @__PURE__ */ new Set();
1900
1780
  for (const section of sections) {
@@ -1904,49 +1784,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1904
1784
  for (const name of names) {
1905
1785
  if (!seenPatterns.has(name)) {
1906
1786
  seenPatterns.add(name);
1907
- try {
1908
- const patResult = await registry.fetchPattern(name);
1909
- if (patResult?.data) {
1910
- const inner = patResult.data;
1911
- const defaultPreset = inner.default_preset || "standard";
1912
- const preset = inner.presets?.[defaultPreset];
1913
- let slots = preset?.layout?.slots || {};
1914
- if (Object.keys(slots).length === 0) {
1915
- const synthetic = generateSyntheticSlots(name, inner.description || "");
1916
- if (Object.keys(synthetic).length > 0) {
1917
- slots = synthetic;
1918
- }
1919
- }
1920
- const spec = {
1921
- description: inner.description || "",
1922
- components: inner.components || [],
1923
- slots,
1924
- layout_hints: inner.layout_hints,
1925
- visual_brief: inner.visual_brief,
1926
- composition: inner.composition,
1927
- motion: inner.motion,
1928
- responsive: inner.responsive,
1929
- accessibility: inner.accessibility
1930
- };
1931
- if (!spec.components || spec.components.length === 0) {
1932
- const syntheticComps = generateSyntheticComponents(name, spec.description);
1933
- if (syntheticComps.length > 0) spec.components = syntheticComps;
1934
- }
1935
- patternSpecs[name] = spec;
1936
- } else {
1937
- const synthetic = generateSyntheticSlots(name, "");
1938
- const syntheticComps = generateSyntheticComponents(name, "");
1939
- if (Object.keys(synthetic).length > 0 || syntheticComps.length > 0) {
1940
- patternSpecs[name] = { description: "", components: syntheticComps, slots: synthetic };
1941
- }
1942
- }
1943
- } catch {
1944
- const synthetic = generateSyntheticSlots(name, "");
1945
- const syntheticComps = generateSyntheticComponents(name, "");
1946
- if (Object.keys(synthetic).length > 0 || syntheticComps.length > 0) {
1947
- patternSpecs[name] = { description: "", components: syntheticComps, slots: synthetic };
1948
- }
1949
- }
1787
+ const spec = await resolvePatternSpec(name, registry, prefetchedSpecs?.[name], true);
1788
+ if (spec) patternSpecs[name] = spec;
1950
1789
  }
1951
1790
  }
1952
1791
  }
@@ -2047,10 +1886,18 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
2047
1886
  writeFileSync(sectionContextPath, contextContent);
2048
1887
  contextFiles.push(sectionContextPath);
2049
1888
  }
1889
+ let projectVoice;
1890
+ if (existsSync(projectJsonFilePath)) {
1891
+ try {
1892
+ const projData = JSON.parse(readFileSync(projectJsonFilePath, "utf-8"));
1893
+ if (projData.voice) projectVoice = projData.voice;
1894
+ } catch {
1895
+ }
1896
+ }
2050
1897
  const routes = blueprint.routes || {};
2051
1898
  const scaffoldContent = generateScaffoldContext({
2052
1899
  appName: essence.meta.archetype || "Application",
2053
- blueprintId: "",
1900
+ blueprintId: storedBlueprintId || essence.meta.blueprint || "",
2054
1901
  themeName,
2055
1902
  personality,
2056
1903
  topologyMarkdown,
@@ -2058,7 +1905,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
2058
1905
  routes,
2059
1906
  constraints: essence.dna.constraints,
2060
1907
  seo: essence.meta.seo,
2061
- navigation: essence.meta.navigation
1908
+ navigation: essence.meta.navigation,
1909
+ voice: projectVoice
2062
1910
  });
2063
1911
  const scaffoldMdPath = join(contextDir, "scaffold.md");
2064
1912
  writeFileSync(scaffoldMdPath, scaffoldContent);
@@ -2074,6 +1922,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
2074
1922
  description: `${essence.meta.archetype || "Application"} section`,
2075
1923
  pages
2076
1924
  };
1925
+ const prefetchedSpecs = options?.patternSpecs;
2077
1926
  const patternSpecs = {};
2078
1927
  const seenPatterns = /* @__PURE__ */ new Set();
2079
1928
  for (const page of pages) {
@@ -2082,44 +1931,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
2082
1931
  for (const name of names) {
2083
1932
  if (!seenPatterns.has(name)) {
2084
1933
  seenPatterns.add(name);
2085
- try {
2086
- const patResult = await registry.fetchPattern(name);
2087
- if (patResult?.data) {
2088
- const inner = patResult.data;
2089
- const defaultPreset = inner.default_preset || "standard";
2090
- const preset = inner.presets?.[defaultPreset];
2091
- let slots = preset?.layout?.slots || {};
2092
- if (Object.keys(slots).length === 0) {
2093
- const synthetic = generateSyntheticSlots(name, inner.description || "");
2094
- if (Object.keys(synthetic).length > 0) {
2095
- slots = synthetic;
2096
- }
2097
- }
2098
- const spec = {
2099
- description: inner.description || "",
2100
- components: inner.components || [],
2101
- slots,
2102
- layout_hints: inner.layout_hints
2103
- };
2104
- if (!spec.components || spec.components.length === 0) {
2105
- const syntheticComps = generateSyntheticComponents(name, spec.description);
2106
- if (syntheticComps.length > 0) spec.components = syntheticComps;
2107
- }
2108
- patternSpecs[name] = spec;
2109
- } else {
2110
- const synthetic = generateSyntheticSlots(name, "");
2111
- const syntheticComps = generateSyntheticComponents(name, "");
2112
- if (Object.keys(synthetic).length > 0 || syntheticComps.length > 0) {
2113
- patternSpecs[name] = { description: "", components: syntheticComps, slots: synthetic };
2114
- }
2115
- }
2116
- } catch {
2117
- const synthetic = generateSyntheticSlots(name, "");
2118
- const syntheticComps = generateSyntheticComponents(name, "");
2119
- if (Object.keys(synthetic).length > 0 || syntheticComps.length > 0) {
2120
- patternSpecs[name] = { description: "", components: syntheticComps, slots: synthetic };
2121
- }
2122
- }
1934
+ const spec = await resolvePatternSpec(name, registry, prefetchedSpecs?.[name], false);
1935
+ if (spec) patternSpecs[name] = spec;
2123
1936
  }
2124
1937
  }
2125
1938
  }
@@ -2306,7 +2119,8 @@ function generateSectionContext(input) {
2306
2119
  text: "Body text, headings, primary content",
2307
2120
  "text-muted": "Secondary text, placeholders, labels",
2308
2121
  primary: "Brand color, key interactive, selected states",
2309
- "primary-hover": "Hover state for primary elements"
2122
+ "primary-hover": "Hover state for primary elements",
2123
+ secondary: "Secondary brand color, supporting elements"
2310
2124
  };
2311
2125
  if (input.themeData?.palette) {
2312
2126
  const modeKey = input.themeMode || "dark";
@@ -2317,12 +2131,36 @@ function generateSectionContext(input) {
2317
2131
  }
2318
2132
  if (input.themeData?.seed?.accent) {
2319
2133
  lines.push(`| \`--d-accent\` | \`${input.themeData.seed.accent}\` | CTAs, links, active states, glow effects |`);
2134
+ const accentGlowVal = input.themeData?.palette?.["accent-glow"]?.[input.themeMode || "dark"] || input.themeData?.tokens?.base?.["accent-glow"];
2135
+ if (accentGlowVal) {
2136
+ lines.push(`| \`--d-accent-glow\` | \`${accentGlowVal}\` | Ambient glow effect around accent elements |`);
2137
+ }
2320
2138
  }
2321
2139
  lines.push("");
2322
2140
  lines.push("Full token set: `src/styles/tokens.css`");
2323
2141
  lines.push("");
2324
2142
  lines.push("**Visual Treatments:** All 6 base treatments available (see DECANTR.md for usage).");
2325
- if (decorators.length > 0) {
2143
+ const decoratorDefs = input.themeData?.decorator_definitions;
2144
+ if (decoratorDefs && Object.keys(decoratorDefs).length > 0) {
2145
+ lines.push("**Theme decorators:**");
2146
+ lines.push("");
2147
+ lines.push("| Class | Intent | Key CSS | Pairs with |");
2148
+ lines.push("|-------|--------|---------|------------|");
2149
+ for (const [name, def] of Object.entries(decoratorDefs)) {
2150
+ const intent = def.intent || "";
2151
+ const cssProps = def.css ? Object.entries(def.css).map(([p, v]) => `${p}: ${v}`).join("; ") : "";
2152
+ const pairsWith = def.pairs_with || "";
2153
+ lines.push(`| \`.${name}\` | ${intent} | ${cssProps} | ${pairsWith} |`);
2154
+ }
2155
+ lines.push("");
2156
+ lines.push("**Decorator usage guide:**");
2157
+ for (const [name, def] of Object.entries(decoratorDefs)) {
2158
+ if (def.usage && def.usage.length > 0) {
2159
+ lines.push(`- \`.${name}\`: ${def.usage.join(", ")}`);
2160
+ }
2161
+ }
2162
+ lines.push("");
2163
+ } else if (decorators.length > 0) {
2326
2164
  lines.push("**Theme decorators:**");
2327
2165
  lines.push("");
2328
2166
  lines.push("| Class | Usage |");
@@ -2332,12 +2170,16 @@ function generateSectionContext(input) {
2332
2170
  }
2333
2171
  lines.push("");
2334
2172
  } else {
2335
- lines.push("**Theme decorators:** None defined for this theme.");
2173
+ lines.push("No theme decorators defined.");
2336
2174
  lines.push("");
2337
2175
  }
2338
2176
  if (themeHints) {
2339
2177
  if (themeHints.preferred && themeHints.preferred.length > 0) {
2340
- lines.push(`**Preferred:** ${themeHints.preferred.join(", ")}`);
2178
+ const sectionPatterns = new Set(section.pages.flatMap((p) => p.layout.map((l) => typeof l === "string" ? l : l.pattern)));
2179
+ const relevant = themeHints.preferred.filter((p) => sectionPatterns.has(p));
2180
+ if (relevant.length > 0) {
2181
+ lines.push(`**Preferred:** ${relevant.join(", ")}`);
2182
+ }
2341
2183
  }
2342
2184
  if (themeHints.compositions) {
2343
2185
  lines.push(`**Compositions:** ${themeHints.compositions}`);
@@ -9,7 +9,7 @@ import {
9
9
  scaffoldMinimal,
10
10
  scaffoldProject,
11
11
  syncRegistry
12
- } from "./chunk-PMBLHVBX.js";
12
+ } from "./chunk-5MKMLONH.js";
13
13
 
14
14
  // src/index.ts
15
15
  import { readFileSync as readFileSync15, existsSync as existsSync23, readdirSync as readdirSync6 } from "fs";
@@ -4332,7 +4332,7 @@ async function main() {
4332
4332
  break;
4333
4333
  }
4334
4334
  case "upgrade": {
4335
- const { cmdUpgrade } = await import("./upgrade-2OG7UKTR.js");
4335
+ const { cmdUpgrade } = await import("./upgrade-3AAWHJGG.js");
4336
4336
  const applyFlag = args.includes("--apply");
4337
4337
  await cmdUpgrade(process.cwd(), { apply: applyFlag });
4338
4338
  break;
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import "./chunk-DESYSWLL.js";
2
- import "./chunk-PMBLHVBX.js";
1
+ import "./chunk-HWMN432I.js";
2
+ import "./chunk-5MKMLONH.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  RegistryClient,
3
3
  refreshDerivedFiles
4
- } from "./chunk-PMBLHVBX.js";
4
+ } from "./chunk-5MKMLONH.js";
5
5
 
6
6
  // src/commands/upgrade.ts
7
7
  import { readFileSync, writeFileSync, existsSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decantr/cli",
3
- "version": "1.6.1",
3
+ "version": "1.6.3",
4
4
  "description": "Decantr CLI — search the registry, validate essence files, and access design intelligence from the terminal",
5
5
  "license": "MIT",
6
6
  "repository": {