@decantr/cli 1.5.3 → 1.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.
@@ -5,7 +5,7 @@ import { fileURLToPath } from "url";
5
5
  import { computeSpatialTokens } from "@decantr/essence-spec";
6
6
 
7
7
  // src/treatments.ts
8
- function generateTreatmentCSS(spatialTokens, treatmentOverrides, recipeDecorators, recipeName) {
8
+ function generateTreatmentCSS(spatialTokens, treatmentOverrides, themeDecorators, themeName) {
9
9
  const lines = [];
10
10
  lines.push("/* Generated by @decantr/cli \u2014 Visual Treatment System */");
11
11
  lines.push("");
@@ -93,6 +93,9 @@ function generateTreatmentCSS(spatialTokens, treatmentOverrides, recipeDecorator
93
93
  ["box-shadow", "var(--d-shadow-lg)"],
94
94
  ["z-index", "50"]
95
95
  ]);
96
+ emitRule(".d-surface[data-interactive]", [
97
+ ["cursor", "pointer"]
98
+ ]);
96
99
  emitRule(".d-surface[data-interactive]:hover", [
97
100
  ["border-color", "var(--d-primary-hover, var(--d-border))"],
98
101
  ["box-shadow", "var(--d-shadow-md)"],
@@ -154,7 +157,13 @@ function generateTreatmentCSS(spatialTokens, treatmentOverrides, recipeDecorator
154
157
  ["padding", "var(--d-section-py) 0"]
155
158
  ]);
156
159
  lines.push(".d-section + .d-section {");
157
- lines.push(" border-top: 1px solid var(--d-border);");
160
+ lines.push(" border-top: 1px solid transparent;");
161
+ lines.push(" border-image: linear-gradient(to right, transparent, var(--d-border), transparent) 1;");
162
+ lines.push(" margin-top: var(--d-gap-2);");
163
+ lines.push("}");
164
+ lines.push("");
165
+ lines.push('.d-section[data-density="compact"] {');
166
+ lines.push(" padding: calc(var(--d-section-py) * 0.5) 0;");
158
167
  lines.push("}");
159
168
  lines.push("");
160
169
  emitRule(".d-annotation", [
@@ -185,11 +194,19 @@ function generateTreatmentCSS(spatialTokens, treatmentOverrides, recipeDecorator
185
194
  ["background", "color-mix(in srgb, var(--d-info) 15%, transparent)"],
186
195
  ["color", "var(--d-info)"]
187
196
  ]);
188
- if (recipeDecorators && Object.keys(recipeDecorators).length > 0) {
189
- const label = recipeName ? ` (${recipeName})` : "";
190
- lines.push(`/* \u2500\u2500 Layer 3: Recipe Decorators${label} \u2500\u2500 */`);
197
+ emitRule(".d-label", [
198
+ ["font-size", "0.7rem"],
199
+ ["font-weight", "600"],
200
+ ["text-transform", "uppercase"],
201
+ ["letter-spacing", "0.08em"],
202
+ ["color", "var(--d-text-muted)"],
203
+ ["font-family", "var(--d-font-mono, ui-monospace, monospace)"]
204
+ ]);
205
+ if (themeDecorators && Object.keys(themeDecorators).length > 0) {
206
+ const label = themeName ? ` (${themeName})` : "";
207
+ lines.push(`/* \u2500\u2500 Layer 3: Theme Decorators${label} \u2500\u2500 */`);
191
208
  lines.push("");
192
- for (const [name, description] of Object.entries(recipeDecorators)) {
209
+ for (const [name, description] of Object.entries(themeDecorators)) {
193
210
  const rule = generateDecoratorRule(name, description);
194
211
  lines.push(rule);
195
212
  lines.push("");
@@ -208,6 +225,35 @@ function generateTreatmentCSS(spatialTokens, treatmentOverrides, recipeDecorator
208
225
  lines.push("}");
209
226
  return lines.join("\n");
210
227
  }
228
+ function generatePersonalityCSS(personality, themeData) {
229
+ const text = personality.join(" ").toLowerCase();
230
+ const rules = [];
231
+ if (text.includes("neon") || text.includes("glow")) {
232
+ const glowColor = "var(--d-accent-glow, rgba(0, 212, 255, 0.3))";
233
+ rules.push(`.neon-glow { box-shadow: 0 0 20px ${glowColor}; }`);
234
+ rules.push(`.neon-glow-hover:hover { box-shadow: 0 0 24px ${glowColor}; transition: box-shadow var(--d-duration-hover, 0.15s) var(--d-easing, ease); }`);
235
+ rules.push(`.neon-text-glow { text-shadow: 0 0 12px ${glowColor}; }`);
236
+ rules.push(`.neon-border-glow { border-color: var(--d-accent); box-shadow: 0 0 8px ${glowColor}; }`);
237
+ }
238
+ if (text.includes("monospace") || text.includes("mono")) {
239
+ rules.push(`.mono-data { font-family: var(--d-font-mono, ui-monospace, monospace); font-variant-numeric: tabular-nums; }`);
240
+ }
241
+ if (text.includes("pulse") || text.includes("ring") || text.includes("status")) {
242
+ rules.push(`.status-ring { width: 48px; height: 48px; border-radius: 50%; border: 2px solid var(--d-border); display: flex; align-items: center; justify-content: center; position: relative; transition: border-color 0.2s ease, box-shadow 0.2s ease; }`);
243
+ rules.push(`.status-ring[data-status="active"] { border-color: var(--d-success); }`);
244
+ 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); }`);
245
+ rules.push(`.status-ring[data-status="warning"] { border-color: var(--d-warning); }`);
246
+ rules.push(`.status-ring[data-status="idle"] { border-color: var(--d-text-muted); }`);
247
+ rules.push(`@keyframes pulse-ring { 0% { opacity: 0.6; transform: scale(1); } 100% { opacity: 0; transform: scale(1.3); } }`);
248
+ 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; }`);
249
+ }
250
+ if (themeData.motion?.entrance) {
251
+ rules.push(`.entrance-fade { animation: decantr-entrance var(--d-duration-entrance, 0.2s) var(--d-easing, ease-out); }`);
252
+ rules.push(`@keyframes decantr-entrance { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }`);
253
+ }
254
+ if (rules.length === 0) return "";
255
+ return "\n/* \u2500\u2500 Personality-Derived Utilities \u2500\u2500 */\n\n" + rules.join("\n\n") + "\n";
256
+ }
211
257
 
212
258
  // src/scaffold.ts
213
259
  var __dirname = dirname(fileURLToPath(import.meta.url));
@@ -520,6 +566,21 @@ function generateTokensCSS(themeData, mode, spatialTokens) {
520
566
  };
521
567
  }
522
568
  const tokens = buildTokens(resolvedMode);
569
+ if (themeData?.typography?.mono) {
570
+ tokens["--d-font-mono"] = themeData.typography.mono;
571
+ }
572
+ if (themeData?.palette?.["accent-glow"]?.[resolvedMode]) {
573
+ tokens["--d-accent-glow"] = themeData.palette["accent-glow"][resolvedMode];
574
+ }
575
+ if (themeData?.motion?.durations?.hover) {
576
+ tokens["--d-duration-hover"] = themeData.motion.durations.hover;
577
+ }
578
+ if (themeData?.motion?.durations?.entrance) {
579
+ tokens["--d-duration-entrance"] = themeData.motion.durations.entrance;
580
+ }
581
+ if (themeData?.motion?.timing) {
582
+ tokens["--d-easing"] = themeData.motion.timing;
583
+ }
523
584
  const lines = Object.entries(tokens).map(([key, value]) => ` ${key}: ${value};`).join("\n");
524
585
  const spatialLines = spatialTokens ? "\n" + Object.entries(spatialTokens).map(([k, v]) => ` ${k}: ${v};`).join("\n") : "";
525
586
  let css = `/* Generated by @decantr/cli */
@@ -612,35 +673,6 @@ input, button, textarea, select {
612
673
  }
613
674
  `;
614
675
  }
615
- function generateTreatmentsContext(recipeData, recipeName) {
616
- const lines = [];
617
- lines.push(`# Visual Treatments: ${recipeName}`);
618
- lines.push("");
619
- lines.push("## Base Treatments");
620
- lines.push("");
621
- lines.push("d-interactive, d-surface, d-data, d-control, d-section, d-annotation \u2014 see DECANTR.md for usage.");
622
- lines.push("");
623
- if (recipeData?.decorators && Object.keys(recipeData.decorators).length > 0) {
624
- lines.push(`## Recipe Decorators (${recipeName}-specific)`);
625
- lines.push("");
626
- lines.push("| Class | Use for |");
627
- lines.push("|-------|---------|");
628
- for (const [name, description] of Object.entries(recipeData.decorators)) {
629
- const useFor = description.split(".")[0].trim();
630
- lines.push(`| ${name} | ${useFor} |`);
631
- }
632
- lines.push("");
633
- }
634
- lines.push("## Composition");
635
- lines.push("");
636
- lines.push("Atoms + treatment + recipe decorator:");
637
- lines.push("```tsx");
638
- lines.push(`css('_flex _col _gap4') + ' d-surface'`);
639
- lines.push("```");
640
- lines.push("");
641
- lines.push("Atoms use `css()` function. Treatments and recipe decorators are plain class strings.");
642
- return lines.join("\n");
643
- }
644
676
  function generateDecoratorRule(name, description) {
645
677
  const rules = [];
646
678
  const descLower = description.toLowerCase();
@@ -876,7 +908,7 @@ function buildEssence(options, archetypeData) {
876
908
  style: options.theme,
877
909
  mode: options.mode,
878
910
  recipe: options.theme,
879
- // Recipe defaults to theme
911
+ // Legacy v2 field — kept for backward compatibility
880
912
  shape: options.shape
881
913
  },
882
914
  personality: options.personality,
@@ -889,6 +921,7 @@ function buildEssence(options, archetypeData) {
889
921
  guard: {
890
922
  enforce_style: true,
891
923
  enforce_recipe: true,
924
+ // Legacy v2 field — kept for backward compatibility
892
925
  mode: options.guard
893
926
  },
894
927
  density: {
@@ -902,7 +935,7 @@ function buildEssence(options, archetypeData) {
902
935
  }
903
936
  return essence;
904
937
  }
905
- function buildEssenceV3(options, archetypeData, themeHints, recipeHints) {
938
+ function buildEssenceV3(options, archetypeData, themeHints) {
906
939
  let pages = [
907
940
  { id: "home", layout: ["hero"] }
908
941
  ];
@@ -939,9 +972,8 @@ function buildEssenceV3(options, archetypeData, themeHints, recipeHints) {
939
972
  };
940
973
  const dna = {
941
974
  theme: {
942
- style: options.theme,
975
+ id: options.theme,
943
976
  mode: options.mode,
944
- recipe: options.theme,
945
977
  shape: options.shape
946
978
  },
947
979
  spacing: {
@@ -951,9 +983,9 @@ function buildEssenceV3(options, archetypeData, themeHints, recipeHints) {
951
983
  content_gap: densityLevelMap[options.density] || "_gap4"
952
984
  },
953
985
  typography: {
954
- scale: themeHints?.typography_hints?.scale || "modular",
955
- heading_weight: themeHints?.typography_hints?.heading_weight || 600,
956
- body_weight: themeHints?.typography_hints?.body_weight || 400
986
+ scale: themeHints?.typography?.scale || "modular",
987
+ heading_weight: themeHints?.typography?.heading_weight || 600,
988
+ body_weight: themeHints?.typography?.body_weight || 400
957
989
  },
958
990
  color: {
959
991
  palette: "semantic",
@@ -961,17 +993,17 @@ function buildEssenceV3(options, archetypeData, themeHints, recipeHints) {
961
993
  cvd_preference: options.accessibility?.cvd_preference || "auto"
962
994
  },
963
995
  radius: {
964
- philosophy: recipeHints?.radius_hints?.philosophy || options.shape,
965
- base: recipeHints?.radius_hints?.base || shapeRadiusMap[options.shape] || 8
996
+ philosophy: themeHints?.radius?.philosophy || options.shape,
997
+ base: themeHints?.radius?.base || shapeRadiusMap[options.shape] || 8
966
998
  },
967
999
  elevation: {
968
1000
  system: "layered",
969
1001
  max_levels: 3
970
1002
  },
971
1003
  motion: {
972
- preference: recipeHints?.animation?.preference || themeHints?.motion_hints?.preference || "subtle",
1004
+ preference: themeHints?.motion?.preference || "subtle",
973
1005
  duration_scale: 1,
974
- reduce_motion: themeHints?.motion_hints?.reduce_motion_default ?? true
1006
+ reduce_motion: themeHints?.motion?.reduce_motion ?? true
975
1007
  },
976
1008
  accessibility: {
977
1009
  wcag_level: options.accessibility?.wcag_level || "AA",
@@ -1003,21 +1035,21 @@ function buildEssenceV3(options, archetypeData, themeHints, recipeHints) {
1003
1035
  }
1004
1036
  var CSS_APPROACH_CONTENT = `## CSS Implementation
1005
1037
 
1006
- This project uses **@decantr/css** for layout atoms, **visual treatments** for semantic styling, and **recipe decorators** for theme-specific decoration.
1038
+ This project uses **@decantr/css** for layout atoms, **visual treatments** for semantic styling, and **theme decorators** for theme-specific decoration.
1007
1039
 
1008
1040
  ### Three File Setup
1009
1041
 
1010
1042
  \`\`\`
1011
1043
  src/styles/
1012
1044
  tokens.css # Design tokens: --d-primary, --d-surface, --d-bg, etc.
1013
- treatments.css # Visual treatments (d-interactive, d-surface, ...) + recipe decorators
1045
+ treatments.css # Visual treatments (d-interactive, d-surface, ...) + theme decorators
1014
1046
  global.css # Resets, base typography, sr-only
1015
1047
  \`\`\`
1016
1048
 
1017
1049
  \`\`\`javascript
1018
1050
  import { css } from '@decantr/css'; // Atoms runtime
1019
1051
  import './styles/tokens.css'; // Theme tokens
1020
- import './styles/treatments.css'; // Treatments + recipe decorators
1052
+ import './styles/treatments.css'; // Treatments + theme decorators
1021
1053
  import './styles/global.css'; // Resets
1022
1054
  \`\`\`
1023
1055
 
@@ -1036,7 +1068,7 @@ Six base treatment classes provide semantic styling. Combine with atoms for layo
1036
1068
 
1037
1069
  ### Composition
1038
1070
 
1039
- Atoms + treatment + recipe decorator:
1071
+ Atoms + treatment + theme decorator:
1040
1072
 
1041
1073
  \`\`\`tsx
1042
1074
  <button className={css('_px4 _py2') + ' d-interactive'} data-variant="primary">Deploy</button>
@@ -1046,37 +1078,60 @@ Atoms + treatment + recipe decorator:
1046
1078
 
1047
1079
  - **Atoms:** \`css('_flex _col _gap4')\` \u2014 processed by @decantr/css runtime
1048
1080
  - **Treatments:** \`d-interactive\`, \`d-surface\` \u2014 semantic base styles from treatments.css
1049
- - **Recipe decorators:** \`carbon-glass\`, \`carbon-code\` \u2014 theme-specific decoration from treatments.css
1081
+ - **Theme decorators:** \`carbon-glass\`, \`carbon-code\` \u2014 theme-specific decoration from treatments.css
1050
1082
  - **Combined:** \`css('_flex _col') + ' d-surface carbon-card'\`
1051
1083
 
1052
1084
  ### Atoms Quick Reference
1053
1085
 
1054
1086
  | Category | Examples | Purpose |
1055
- |----------|----------|---------|
1087
+ |----------|---------|---------|
1056
1088
  | Layout | \`_flex\`, \`_col\`, \`_row\`, \`_wrap\`, \`_grid\` | Flex/grid containers |
1057
1089
  | Spacing | \`_gap4\`, \`_p4\`, \`_px4\`, \`_py2\`, \`_m0\` | Gaps, padding, margin |
1058
- | Sizing | \`_w100\`, \`_h100\`, \`_minw0\`, \`_maxwfull\` | Width, height |
1059
- | Text | \`_textlg\`, \`_text2xl\`, \`_fontbold\`, \`_textc\` | Typography |
1090
+ | Sizing | \`_w100\`, \`_h100\`, \`_mw640\`, \`_mw480\` | Width, height, max-width |
1091
+ | Text | \`_textlg\`, \`_text2xl\`, \`_fontbold\`, \`_fontmono\` | Typography |
1092
+ | Color | \`_fgaccent\`, \`_fgmuted\`, \`_fgsuccess\`, \`_bgsurf\` | Foreground/background |
1060
1093
  | Alignment | \`_aic\`, \`_jcc\`, \`_jcsb\`, \`_pic\` | Flex/grid alignment |
1061
1094
  | Position | \`_rel\`, \`_abs\`, \`_sticky\`, \`_z10\` | Positioning |
1062
1095
  | Visual | \`_rounded\`, \`_shadow\`, \`_trans\`, \`_op50\` | Decoration |
1063
- | Color | \`_bgprimary\`, \`_fgtext\`, \`_fgmuted\`, \`_bcborder\` | Theme colors |
1064
1096
  | Responsive | \`_md:gc2\`, \`_lg:gc4\`, \`_sm:flex\` | Breakpoint prefixes |
1097
+ | Cursor | \`_pointer\` | Interaction hints |
1065
1098
 
1066
1099
  Scale: 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24. Example: \`_gap4\` = \`gap:1rem\`.
1067
1100
 
1101
+ ### Section Labels
1102
+
1103
+ Use the d-label class for uppercase section headings.
1104
+ Anchor with a left accent border: \`border-left: 2px solid var(--d-accent); padding-left: 0.5rem\`.
1105
+
1106
+ ### Empty States
1107
+
1108
+ Every data-driven section should handle zero-data gracefully.
1109
+ Pattern: centered 48px muted icon + descriptive message + optional CTA button.
1110
+
1111
+ ### Page Transitions
1112
+
1113
+ If the theme provides motion tokens, apply the \`entrance-fade\` class to page content containers for smooth page-to-page transitions.
1114
+
1115
+ ### Navigation Shortcuts
1116
+
1117
+ If the essence defines hotkeys or command_palette, implement as keyboard event listeners (useEffect + keydown) \u2014 not as visible UI text.
1118
+
1068
1119
  ### Design Tokens
1069
1120
 
1070
- | Token | Purpose |
1071
- |-------|---------|
1072
- | \`--d-primary\` | Primary brand color |
1073
- | \`--d-surface\`, \`--d-surface-raised\` | Surface backgrounds |
1074
- | \`--d-bg\` | Page background |
1075
- | \`--d-border\` | Border color |
1076
- | \`--d-text\`, \`--d-text-muted\` | Text colors |
1077
- | \`--d-success\`, \`--d-error\`, \`--d-warning\`, \`--d-info\` | Status colors |
1078
- | \`--d-shadow\`, \`--d-shadow-lg\` | Elevation shadows |
1079
- | \`--d-radius\`, \`--d-radius-lg\` | Border radii |
1121
+ | Token | Purpose | Use for |
1122
+ |-------|---------|---------|
1123
+ | \`--d-primary\` | Primary brand color | Buttons, links, focus rings |
1124
+ | \`--d-surface\`, \`--d-surface-raised\` | Surface backgrounds | Cards, panels |
1125
+ | \`--d-bg\` | Page background | Body, main container |
1126
+ | \`--d-border\` | Border color | Dividers, card borders |
1127
+ | \`--d-text\`, \`--d-text-muted\` | Text colors | Body text, secondary text |
1128
+ | \`--d-success\`, \`--d-error\`, \`--d-warning\`, \`--d-info\` | Status colors | Alerts, badges, toasts |
1129
+ | \`--d-shadow\`, \`--d-shadow-lg\` | Elevation shadows | Cards, overlays |
1130
+ | \`--d-radius\`, \`--d-radius-lg\` | Border radii | Buttons, cards |
1131
+ | \`--d-font-mono\` | Monospace font stack | Code, metrics, data |
1132
+ | \`--d-duration-hover\` | Hover transition | Interactive elements |
1133
+ | \`--d-easing\` | Animation easing | All transitions |
1134
+ | \`--d-accent-glow\` | Glow color | Hover effects, focus rings |
1080
1135
 
1081
1136
  ### Routing
1082
1137
 
@@ -1113,8 +1168,7 @@ function generateProjectJson(detected, options, registrySource) {
1113
1168
  cachedContent: {
1114
1169
  archetypes: [],
1115
1170
  patterns: [],
1116
- themes: [],
1117
- recipes: []
1171
+ themes: []
1118
1172
  }
1119
1173
  },
1120
1174
  initialized: {
@@ -1147,7 +1201,7 @@ function generateTaskContext(templateName, essence) {
1147
1201
  TARGET: essence.target,
1148
1202
  THEME_STYLE: essence.theme.style,
1149
1203
  THEME_MODE: essence.theme.mode,
1150
- THEME_RECIPE: essence.theme.recipe,
1204
+ THEME_RECIPE: essence.theme.style,
1151
1205
  DEFAULT_SHELL: defaultShell,
1152
1206
  GUARD_MODE: essence.guard.mode,
1153
1207
  LAYOUT: layout,
@@ -1171,9 +1225,9 @@ function generateTaskContextV3(templateName, essence) {
1171
1225
  const contentGap = essence.dna.spacing?.content_gap || "_gap4";
1172
1226
  const vars = {
1173
1227
  TARGET: essence.meta.target || "react",
1174
- THEME_STYLE: essence.dna.theme.style,
1228
+ THEME_STYLE: essence.dna.theme.id || essence.dna.theme.style || "",
1175
1229
  THEME_MODE: essence.dna.theme.mode,
1176
- THEME_RECIPE: essence.dna.theme.recipe || essence.dna.theme.style,
1230
+ THEME_RECIPE: essence.dna.theme.id || essence.dna.theme.style || "",
1177
1231
  DEFAULT_SHELL: defaultShell,
1178
1232
  GUARD_MODE: essence.meta.guard.mode,
1179
1233
  LAYOUT: layout,
@@ -1210,9 +1264,9 @@ ${rows.join("\n")}`;
1210
1264
  BLUEPRINT: "",
1211
1265
  PERSONALITY: (essence.dna.personality || []).join(", "),
1212
1266
  TARGET: essence.meta.target ?? "",
1213
- THEME_STYLE: essence.dna.theme.style,
1267
+ THEME_STYLE: essence.dna.theme.id ?? essence.dna.theme.style ?? "",
1214
1268
  THEME_MODE: essence.dna.theme.mode,
1215
- THEME_RECIPE: essence.dna.theme.recipe ?? "",
1269
+ THEME_RECIPE: essence.dna.theme.id ?? essence.dna.theme.style ?? "",
1216
1270
  SHAPE: essence.dna.theme.shape ?? "",
1217
1271
  PAGES_TABLE: pagesTable,
1218
1272
  FEATURES_LIST: featuresList,
@@ -1247,8 +1301,8 @@ ${cacheEntry}
1247
1301
  return true;
1248
1302
  }
1249
1303
  }
1250
- async function scaffoldProject(projectRoot, options, detected, registry, archetypeData, registrySource = "cache", themeData, recipeData, topologyMarkdown, composedSections, routeMap, patternSpecs, blueprintData) {
1251
- const essenceV3 = buildEssenceV3(options, archetypeData, themeData, recipeData);
1304
+ async function scaffoldProject(projectRoot, options, detected, registry, archetypeData, registrySource = "cache", themeData, topologyMarkdown, composedSections, routeMap, patternSpecs, blueprintData) {
1305
+ const essenceV3 = buildEssenceV3(options, archetypeData, themeData);
1252
1306
  const essence = buildEssence(options, archetypeData);
1253
1307
  const decantrDir = join(projectRoot, ".decantr");
1254
1308
  const contextDir = join(decantrDir, "context");
@@ -1284,7 +1338,7 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
1284
1338
  }
1285
1339
  writeFileSync(essencePath, JSON.stringify(essenceV3, null, 2) + "\n");
1286
1340
  }
1287
- const refreshResult = await refreshDerivedFiles(projectRoot, essenceV3, registry, themeData, recipeData, { isInitialScaffold: true });
1341
+ const refreshResult = await refreshDerivedFiles(projectRoot, essenceV3, registry, themeData, { isInitialScaffold: true });
1288
1342
  contextFiles.push(...refreshResult.contextFiles);
1289
1343
  const gitignoreUpdated = updateGitignore(projectRoot);
1290
1344
  return {
@@ -1299,7 +1353,7 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
1299
1353
  function scaffoldMinimal(projectRoot) {
1300
1354
  const decantrDir = join(projectRoot, ".decantr");
1301
1355
  const customDir = join(decantrDir, "custom");
1302
- const contentTypes = ["patterns", "recipes", "themes", "blueprints", "archetypes", "shells"];
1356
+ const contentTypes = ["patterns", "themes", "blueprints", "archetypes", "shells"];
1303
1357
  for (const type of contentTypes) {
1304
1358
  mkdirSync(join(customDir, type), { recursive: true });
1305
1359
  }
@@ -1307,9 +1361,8 @@ function scaffoldMinimal(projectRoot) {
1307
1361
  version: "3.0.0",
1308
1362
  dna: {
1309
1363
  theme: {
1310
- style: "default",
1364
+ id: "default",
1311
1365
  mode: "dark",
1312
- recipe: "default",
1313
1366
  shape: "rounded"
1314
1367
  },
1315
1368
  spacing: {
@@ -1391,8 +1444,7 @@ function scaffoldMinimal(projectRoot) {
1391
1444
  cachedContent: {
1392
1445
  archetypes: [],
1393
1446
  patterns: [],
1394
- themes: [],
1395
- recipes: []
1447
+ themes: []
1396
1448
  }
1397
1449
  },
1398
1450
  initialized: {
@@ -1486,12 +1538,11 @@ When available, use these tools:
1486
1538
  gitignoreUpdated
1487
1539
  };
1488
1540
  }
1489
- async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThemeData, prefetchedRecipeData, options) {
1541
+ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThemeData, options) {
1490
1542
  const decantrDir = join(projectRoot, ".decantr");
1491
1543
  const contextDir = join(decantrDir, "context");
1492
1544
  mkdirSync(contextDir, { recursive: true });
1493
- const themeName = essence.dna.theme.style;
1494
- const recipeName = essence.dna.theme.recipe ?? themeName;
1545
+ const themeName = essence.dna.theme.id || essence.dna.theme.style || "default";
1495
1546
  const mode = essence.dna.theme.mode;
1496
1547
  const guardMode = essence.meta.guard.mode;
1497
1548
  const guardConfig = {
@@ -1501,7 +1552,6 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1501
1552
  };
1502
1553
  const personality = essence.dna.personality || [];
1503
1554
  let themeData = prefetchedThemeData;
1504
- let recipeData = prefetchedRecipeData;
1505
1555
  if (!themeData) try {
1506
1556
  const themeResult = await registry.fetchTheme(themeName);
1507
1557
  if (themeResult?.data) {
@@ -1512,53 +1562,20 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1512
1562
  palette: t.palette,
1513
1563
  cvd_support: t.cvd_support,
1514
1564
  tokens: t.tokens,
1515
- typography_hints: t.typography_hints,
1516
- motion_hints: t.motion_hints
1517
- };
1518
- }
1519
- } catch {
1520
- }
1521
- if (!recipeData) try {
1522
- const recipeResult = await registry.fetchRecipe(recipeName);
1523
- if (recipeResult?.data) {
1524
- const raw = recipeResult.data;
1525
- const r = raw.data ?? raw;
1526
- recipeData = {
1527
- decorators: r.decorators,
1528
- spatial_hints: r.spatial_hints,
1529
- radius_hints: r.radius_hints,
1530
- treatment_overrides: r.treatment_overrides
1565
+ typography: t.typography,
1566
+ motion: t.motion,
1567
+ decorators: t.decorators,
1568
+ treatments: t.treatments,
1569
+ spatial: t.spatial,
1570
+ radius: t.radius,
1571
+ shell: t.shell,
1572
+ effects: t.effects,
1573
+ compositions: t.compositions,
1574
+ pattern_preferences: t.pattern_preferences
1531
1575
  };
1532
- if (!recipeData.decorators && raw.data) {
1533
- const inner = raw.data;
1534
- if (inner.decorators) {
1535
- recipeData.decorators = inner.decorators;
1536
- recipeData.spatial_hints = inner.spatial_hints;
1537
- recipeData.radius_hints = inner.radius_hints;
1538
- }
1539
- }
1540
1576
  }
1541
1577
  } catch {
1542
1578
  }
1543
- if (!recipeData?.decorators || Object.keys(recipeData.decorators).length === 0) {
1544
- try {
1545
- const apiUrl = registry.apiUrl || "https://api.decantr.ai/v1";
1546
- const resp = await fetch(`${apiUrl}/recipes/@official/${recipeName}`);
1547
- if (resp.ok) {
1548
- const apiData = await resp.json();
1549
- const inner = apiData.data ?? apiData;
1550
- if (inner.decorators && Object.keys(inner.decorators).length > 0) {
1551
- recipeData = {
1552
- decorators: inner.decorators,
1553
- spatial_hints: inner.spatial_hints,
1554
- radius_hints: inner.radius_hints,
1555
- treatment_overrides: inner.treatment_overrides
1556
- };
1557
- }
1558
- }
1559
- } catch {
1560
- }
1561
- }
1562
1579
  if (!themeData?.seed?.primary) {
1563
1580
  try {
1564
1581
  const apiUrl = registry.apiUrl || "https://api.decantr.ai/v1";
@@ -1572,8 +1589,16 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1572
1589
  palette: inner.palette,
1573
1590
  cvd_support: inner.cvd_support,
1574
1591
  tokens: inner.tokens,
1575
- typography_hints: inner.typography_hints,
1576
- motion_hints: inner.motion_hints
1592
+ typography: inner.typography,
1593
+ motion: inner.motion,
1594
+ decorators: inner.decorators,
1595
+ treatments: inner.treatments,
1596
+ spatial: inner.spatial,
1597
+ radius: inner.radius,
1598
+ shell: inner.shell,
1599
+ effects: inner.effects,
1600
+ compositions: inner.compositions,
1601
+ pattern_preferences: inner.pattern_preferences
1577
1602
  };
1578
1603
  }
1579
1604
  }
@@ -1583,10 +1608,10 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1583
1608
  const stylesDir = join(projectRoot, "src", "styles");
1584
1609
  mkdirSync(stylesDir, { recursive: true });
1585
1610
  const densityLevel = options.density || "comfortable";
1586
- const spatialTokens = computeSpatialTokens(densityLevel, recipeData?.spatial_hints ? {
1587
- section_padding: recipeData.spatial_hints.section_padding ?? void 0,
1588
- density_bias: typeof recipeData.spatial_hints.density_bias === "number" ? recipeData.spatial_hints.density_bias : void 0,
1589
- content_gap_shift: recipeData.spatial_hints.content_gap_shift
1611
+ const spatialTokens = computeSpatialTokens(densityLevel, themeData?.spatial ? {
1612
+ section_padding: themeData.spatial.section_padding ?? void 0,
1613
+ density_bias: typeof themeData.spatial.density_bias === "number" ? themeData.spatial.density_bias : void 0,
1614
+ content_gap_shift: themeData.spatial.content_gap_shift
1590
1615
  } : void 0);
1591
1616
  const tokensPath = join(stylesDir, "tokens.css");
1592
1617
  const hasRealThemeData = themeData?.seed?.primary || themeData?.palette?.background;
@@ -1594,23 +1619,24 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1594
1619
  writeFileSync(tokensPath, generateTokensCSS(themeData, mode, spatialTokens));
1595
1620
  }
1596
1621
  const treatmentsPath = join(stylesDir, "treatments.css");
1597
- writeFileSync(treatmentsPath, generateTreatmentCSS(
1622
+ let treatmentCSS = generateTreatmentCSS(
1598
1623
  spatialTokens,
1599
- recipeData?.treatment_overrides,
1600
- recipeData?.decorators,
1624
+ themeData?.treatments,
1625
+ themeData?.decorators,
1601
1626
  themeName
1602
- ));
1627
+ );
1628
+ const personalityCSS = generatePersonalityCSS(personality || [], themeData || {});
1629
+ treatmentCSS += personalityCSS;
1630
+ writeFileSync(treatmentsPath, treatmentCSS);
1603
1631
  const globalPath = join(stylesDir, "global.css");
1604
1632
  if (!existsSync(globalPath)) {
1605
1633
  writeFileSync(globalPath, generateGlobalCSS(personality));
1606
1634
  }
1607
1635
  const cssFiles = [tokensPath, treatmentsPath, globalPath];
1608
- const treatmentsMdPath = join(contextDir, "treatments.md");
1609
- writeFileSync(treatmentsMdPath, generateTreatmentsContext(recipeData, recipeName));
1610
1636
  const decantrMdPath = join(projectRoot, "DECANTR.md");
1611
1637
  writeFileSync(decantrMdPath, generateDecantrMdV31(guardMode, CSS_APPROACH_CONTENT));
1612
1638
  const hasSections = essence.blueprint.sections && essence.blueprint.sections.length > 0;
1613
- const contextFiles = [treatmentsMdPath];
1639
+ const contextFiles = [];
1614
1640
  if (!hasSections) {
1615
1641
  const summaryPath = join(contextDir, "essence-summary.md");
1616
1642
  writeFileSync(summaryPath, generateEssenceSummaryV3(essence));
@@ -1708,8 +1734,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1708
1734
  const topologyMarkdown = generateTopologySection(topologyData, personality);
1709
1735
  const themeTokensCss = existsSync(tokensPath) ? readFileSync(tokensPath, "utf-8") : "";
1710
1736
  const decoratorList = [];
1711
- if (recipeData?.decorators) {
1712
- for (const [name, desc] of Object.entries(recipeData.decorators)) {
1737
+ if (themeData?.decorators) {
1738
+ for (const [name, desc] of Object.entries(themeData.decorators)) {
1713
1739
  decoratorList.push({ name, description: desc });
1714
1740
  }
1715
1741
  } else if (existsSync(decoratorsPath)) {
@@ -1740,7 +1766,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1740
1766
  shellInfoCache[shellId] = {
1741
1767
  description: inner.description || "",
1742
1768
  regions: inner.config?.regions || [],
1743
- layout: inner.layout || void 0
1769
+ layout: inner.layout || void 0,
1770
+ guidance: inner.guidance || void 0
1744
1771
  };
1745
1772
  }
1746
1773
  } catch {
@@ -1777,7 +1804,6 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1777
1804
  guardConfig,
1778
1805
  personality,
1779
1806
  themeName,
1780
- recipeName,
1781
1807
  zoneContext,
1782
1808
  patternSpecs: sectionPatterns,
1783
1809
  constraints: essence.dna.constraints,
@@ -1792,7 +1818,6 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1792
1818
  appName: essence.meta.archetype || "Application",
1793
1819
  blueprintId: "",
1794
1820
  themeName,
1795
- recipeName,
1796
1821
  personality,
1797
1822
  topologyMarkdown,
1798
1823
  sections,
@@ -1867,8 +1892,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1867
1892
  }
1868
1893
  const themeTokensCss = existsSync(tokensPath) ? readFileSync(tokensPath, "utf-8") : "";
1869
1894
  const decoratorList = [];
1870
- if (recipeData?.decorators) {
1871
- for (const [name, desc] of Object.entries(recipeData.decorators)) {
1895
+ if (themeData?.decorators) {
1896
+ for (const [name, desc] of Object.entries(themeData.decorators)) {
1872
1897
  decoratorList.push({ name, description: desc });
1873
1898
  }
1874
1899
  } else if (existsSync(decoratorsPath)) {
@@ -1894,7 +1919,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1894
1919
  v30ShellInfo = {
1895
1920
  description: inner.description || "",
1896
1921
  regions: inner.config?.regions || [],
1897
- layout: inner.layout || void 0
1922
+ layout: inner.layout || void 0,
1923
+ guidance: inner.guidance || void 0
1898
1924
  };
1899
1925
  }
1900
1926
  } catch {
@@ -1906,7 +1932,6 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1906
1932
  guardConfig,
1907
1933
  personality,
1908
1934
  themeName,
1909
- recipeName,
1910
1935
  zoneContext: `This is the primary section (${shell} shell).`,
1911
1936
  patternSpecs,
1912
1937
  constraints: essence.dna.constraints,
@@ -2010,7 +2035,7 @@ function generateSyntheticComponents(patternId, description) {
2010
2035
  return [...new Set(syntheticComponents)];
2011
2036
  }
2012
2037
  function generateSectionContext(input) {
2013
- const { section, decorators, guardConfig, personality, themeName, recipeName, zoneContext, patternSpecs, recipeHints, constraints, shellInfo } = input;
2038
+ const { section, decorators, guardConfig, personality, themeName, zoneContext, patternSpecs, themeHints, constraints, shellInfo } = input;
2014
2039
  const lines = [];
2015
2040
  lines.push(`# Section: ${section.id}`);
2016
2041
  lines.push("");
@@ -2029,6 +2054,15 @@ function generateSectionContext(input) {
2029
2054
  }
2030
2055
  }
2031
2056
  lines.push("");
2057
+ if (shellInfo?.guidance && Object.keys(shellInfo.guidance).length > 0) {
2058
+ lines.push(`## Shell Notes (${section.shell})`);
2059
+ lines.push("");
2060
+ for (const [key, value] of Object.entries(shellInfo.guidance)) {
2061
+ const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
2062
+ lines.push(`- **${label}:** ${value}`);
2063
+ }
2064
+ lines.push("");
2065
+ }
2032
2066
  lines.push("---");
2033
2067
  lines.push("");
2034
2068
  lines.push(`**Guard:** ${guardConfig.mode} mode | DNA violations = ${guardConfig.dna_enforcement} | Blueprint violations = ${guardConfig.blueprint_enforcement}`);
@@ -2038,18 +2072,18 @@ function generateSectionContext(input) {
2038
2072
  lines.push("**Visual Treatments:** All 6 base treatments available (see DECANTR.md for usage).");
2039
2073
  if (decorators.length > 0) {
2040
2074
  const names = decorators.map((d) => d.name).join(", ");
2041
- lines.push(`**Recipe decorators:** ${names}`);
2075
+ lines.push(`**Theme decorators:** ${names}`);
2042
2076
  }
2043
2077
  lines.push("");
2044
- if (recipeHints) {
2045
- if (recipeHints.preferred && recipeHints.preferred.length > 0) {
2046
- lines.push(`**Preferred:** ${recipeHints.preferred.join(", ")}`);
2078
+ if (themeHints) {
2079
+ if (themeHints.preferred && themeHints.preferred.length > 0) {
2080
+ lines.push(`**Preferred:** ${themeHints.preferred.join(", ")}`);
2047
2081
  }
2048
- if (recipeHints.compositions) {
2049
- lines.push(`**Compositions:** ${recipeHints.compositions}`);
2082
+ if (themeHints.compositions) {
2083
+ lines.push(`**Compositions:** ${themeHints.compositions}`);
2050
2084
  }
2051
- if (recipeHints.spatialHints) {
2052
- lines.push(`**Spatial hints:** ${recipeHints.spatialHints}`);
2085
+ if (themeHints.spatialHints) {
2086
+ lines.push(`**Spatial hints:** ${themeHints.spatialHints}`);
2053
2087
  }
2054
2088
  lines.push("");
2055
2089
  }
@@ -2134,12 +2168,12 @@ function generateSectionContext(input) {
2134
2168
  return lines.join("\n");
2135
2169
  }
2136
2170
  function generateScaffoldContext(input) {
2137
- const { appName, blueprintId, themeName, recipeName, personality, topologyMarkdown, sections, routes, constraints, seo, navigation } = input;
2171
+ const { appName, blueprintId, themeName, personality, topologyMarkdown, sections, routes, constraints, seo, navigation } = input;
2138
2172
  const lines = [];
2139
2173
  lines.push(`# Scaffold: ${appName}`);
2140
2174
  lines.push("");
2141
2175
  lines.push(`**Blueprint:** ${blueprintId}`);
2142
- lines.push(`**Theme:** ${themeName} | **Recipe:** ${recipeName}`);
2176
+ lines.push(`**Theme:** ${themeName}`);
2143
2177
  lines.push(`**Personality:** ${personality.join(", ")}`);
2144
2178
  lines.push("**Guard mode:** creative (no enforcement during initial scaffolding)");
2145
2179
  lines.push("");
@@ -2234,7 +2268,7 @@ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as rea
2234
2268
  import { join as join2 } from "path";
2235
2269
  import { RegistryAPIClient } from "@decantr/registry";
2236
2270
  var DEFAULT_API_URL = "https://api.decantr.ai/v1";
2237
- var ALL_CONTENT_TYPES = ["themes", "patterns", "recipes", "blueprints", "archetypes", "shells"];
2271
+ var ALL_CONTENT_TYPES = ["themes", "patterns", "blueprints", "archetypes", "shells"];
2238
2272
  function loadFromCache(cacheDir, contentType, id, namespace) {
2239
2273
  const nsDir = namespace ? join2(cacheDir, namespace) : cacheDir;
2240
2274
  const cachePath = id ? join2(nsDir, contentType, `${id}.json`) : join2(nsDir, contentType, "index.json");
@@ -2398,12 +2432,6 @@ var RegistryClient = class {
2398
2432
  async fetchShell(id) {
2399
2433
  return this.fetchContentItem("shells", id);
2400
2434
  }
2401
- async fetchRecipes() {
2402
- return this.fetchContentList("recipes");
2403
- }
2404
- async fetchRecipe(id) {
2405
- return this.fetchContentItem("recipes", id);
2406
- }
2407
2435
  /**
2408
2436
  * Check if API is available.
2409
2437
  */