@decantr/cli 1.5.5 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Decantr AI
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.
package/dist/bin.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-4KKFOXBB.js";
3
- import "./chunk-MU3657R7.js";
2
+ import "./chunk-G76RSQRX.js";
3
+ import "./chunk-RG7F5APP.js";
@@ -9,7 +9,7 @@ import {
9
9
  scaffoldMinimal,
10
10
  scaffoldProject,
11
11
  syncRegistry
12
- } from "./chunk-MU3657R7.js";
12
+ } from "./chunk-RG7F5APP.js";
13
13
 
14
14
  // src/index.ts
15
15
  import { readFileSync as readFileSync15, existsSync as existsSync23, readdirSync as readdirSync6 } from "fs";
@@ -2189,6 +2189,7 @@ ${YELLOW5}Next step:${RESET8} Ask your AI assistant to read ${BOLD3}.decantr/ana
2189
2189
  // src/commands/magic.ts
2190
2190
  import { join as join19 } from "path";
2191
2191
  import { existsSync as existsSync19 } from "fs";
2192
+ import * as fs from "fs/promises";
2192
2193
  var BOLD4 = "\x1B[1m";
2193
2194
  var DIM9 = "\x1B[2m";
2194
2195
  var RESET9 = "\x1B[0m";
@@ -2336,7 +2337,32 @@ function parseMagicPrompt(prompt) {
2336
2337
  archetype
2337
2338
  };
2338
2339
  }
2339
- function resolveTheme(hints) {
2340
+ async function resolveTheme(intent, registryClient) {
2341
+ const hints = intent.themeHints;
2342
+ if (registryClient) {
2343
+ try {
2344
+ const themes = await registryClient.fetchThemes();
2345
+ if (themes && themes.length > 0) {
2346
+ const scored = themes.map((t) => {
2347
+ let score = 0;
2348
+ const personality = (t.personality || "").toLowerCase();
2349
+ const tags = (t.tags || []).map((tag) => tag.toLowerCase());
2350
+ for (const hint of intent.themeHints) {
2351
+ if (personality.includes(hint)) score += 3;
2352
+ if (tags.some((tag) => tag.includes(hint))) score += 2;
2353
+ }
2354
+ if (intent.themeHints.includes("dark") && t.modes?.includes("dark")) score += 1;
2355
+ if (intent.themeHints.includes("light") && t.modes?.includes("light")) score += 1;
2356
+ return { id: t.id || t.slug, score, modes: t.modes };
2357
+ }).sort((a, b) => b.score - a.score);
2358
+ if (scored[0] && scored[0].score > 0) {
2359
+ const mode2 = intent.themeHints.includes("light") ? "light" : intent.themeHints.includes("dark") ? "dark" : scored[0].modes?.includes("dark") ? "dark" : "light";
2360
+ return { style: scored[0].id, mode: mode2 };
2361
+ }
2362
+ }
2363
+ } catch {
2364
+ }
2365
+ }
2340
2366
  let mode = "dark";
2341
2367
  if (hints.includes("light")) mode = "light";
2342
2368
  if (hints.includes("neon") || hints.includes("glass")) return { style: "obsidianite", mode };
@@ -2358,6 +2384,18 @@ function buildPersonality(intent) {
2358
2384
  if (intent.themeHints.includes("playful")) return ["friendly", "energetic"];
2359
2385
  return ["professional"];
2360
2386
  }
2387
+ function buildRichPersonality(intent, blueprintData, themeData) {
2388
+ const parts = [];
2389
+ if (blueprintData?.personality && typeof blueprintData.personality === "string")
2390
+ parts.push(blueprintData.personality);
2391
+ if (intent.personalityHints.length > 0 && !parts.some((p) => intent.personalityHints.every((h) => p.toLowerCase().includes(h))))
2392
+ parts.push(`Visual character: ${intent.personalityHints.join(", ")}.`);
2393
+ if (themeData?.personality && !parts.some((p) => p.toLowerCase().includes(themeData.personality.toLowerCase())))
2394
+ parts.push(`Theme influence: ${themeData.personality}.`);
2395
+ if (parts.length === 0)
2396
+ parts.push(`Modern, production-ready ${intent.description}. Clean typography, intentional spacing, polished interactions.`);
2397
+ return parts.join(" ");
2398
+ }
2361
2399
  async function cmdMagic(prompt, projectRoot, options) {
2362
2400
  console.log("");
2363
2401
  console.log(`${MAGENTA}${BOLD4} Decantr Magic${RESET9}`);
@@ -2435,7 +2473,7 @@ async function cmdMagic(prompt, projectRoot, options) {
2435
2473
  } else {
2436
2474
  console.log(dim(" Offline mode \u2014 using defaults."));
2437
2475
  }
2438
- const themeResolved = resolveTheme(intent.themeHints);
2476
+ const themeResolved = await resolveTheme(intent, apiAvailable ? registryClient : void 0);
2439
2477
  const personality = buildPersonality(intent);
2440
2478
  const initOptions = {
2441
2479
  blueprint: matchedBlueprint,
@@ -2463,17 +2501,28 @@ async function cmdMagic(prompt, projectRoot, options) {
2463
2501
  initOptions.personality = [...merged];
2464
2502
  }
2465
2503
  }
2504
+ if (intent.constraints.includes("accessible")) {
2505
+ initOptions.accessibility = { wcag_level: "AA" };
2506
+ }
2507
+ if (intent.constraints.includes("mobile-first")) {
2508
+ initOptions.density = "comfortable";
2509
+ }
2510
+ if (intent.constraints.includes("real-time")) {
2511
+ initOptions.features = [...initOptions.features || [], "websockets", "live-updates"];
2512
+ }
2466
2513
  if (options.dryRun) {
2514
+ const personalityStr = initOptions.personality.join(". ");
2467
2515
  console.log("");
2468
- console.log(`${YELLOW6}${BOLD4} Dry run \u2014 no files written${RESET9}`);
2469
- console.log("");
2470
- console.log(` Blueprint: ${matchedBlueprint || dim("(none \u2014 archetype-based)")}`);
2471
- console.log(` Theme: ${initOptions.theme} (${initOptions.mode})`);
2472
- console.log(` Personality: ${initOptions.personality.join(", ")}`);
2473
- console.log(` Archetype: ${initOptions.archetype}`);
2474
- console.log(` Guard: ${initOptions.guard}`);
2475
- console.log(` Density: ${initOptions.density}`);
2476
- console.log("");
2516
+ console.log(`${BOLD4}decantr magic preview${RESET9}`);
2517
+ console.log("\u2500".repeat(60));
2518
+ console.log(` Blueprint: ${matchedBlueprint || "none (archetype-direct)"}`);
2519
+ console.log(` Theme: ${initOptions.theme} (${initOptions.mode}${initOptions.shape ? ", " + initOptions.shape : ""})`);
2520
+ console.log(` Archetype: ${initOptions.archetype || "auto-detected"}`);
2521
+ console.log(` Personality: ${personalityStr.slice(0, 80)}${personalityStr.length > 80 ? "..." : ""}`);
2522
+ console.log(` WCAG: ${initOptions.accessibility?.wcag_level || "AA"} | Density: ${initOptions.density} | Guard: ${initOptions.guard}`);
2523
+ if (intent.constraints.length > 0)
2524
+ console.log(` Constraints: ${intent.constraints.join(", ")}`);
2525
+ console.log("\u2500".repeat(60));
2477
2526
  return;
2478
2527
  }
2479
2528
  console.log("");
@@ -2625,6 +2674,10 @@ async function cmdMagic(prompt, projectRoot, options) {
2625
2674
  };
2626
2675
  }
2627
2676
  }
2677
+ const richPersonality = buildRichPersonality(intent, blueprintData, themeData);
2678
+ if (richPersonality) {
2679
+ initOptions.personality = [richPersonality];
2680
+ }
2628
2681
  console.log(`${BOLD4} Scaffolding...${RESET9}`);
2629
2682
  const result = await scaffoldProject(
2630
2683
  projectRoot,
@@ -2664,6 +2717,25 @@ async function cmdMagic(prompt, projectRoot, options) {
2664
2717
  if (result.gitignoreUpdated) {
2665
2718
  console.log(` ${dim(".gitignore updated")}`);
2666
2719
  }
2720
+ const contextDir = join19(projectRoot, ".decantr", "context");
2721
+ let sectionCount = 0;
2722
+ try {
2723
+ const files = await fs.readdir(contextDir);
2724
+ sectionCount = files.filter((f) => f.startsWith("section-")).length;
2725
+ } catch {
2726
+ }
2727
+ const treatmentsPath = join19(projectRoot, "src", "styles", "treatments.css");
2728
+ let hasLayers = false;
2729
+ try {
2730
+ const css = await fs.readFile(treatmentsPath, "utf-8");
2731
+ hasLayers = css.includes("@layer");
2732
+ } catch {
2733
+ }
2734
+ console.log(`
2735
+ ${GREEN9}${BOLD4}Quality summary:${RESET9}`);
2736
+ console.log(` Context files: ${sectionCount} sections + scaffold.md + DECANTR.md`);
2737
+ console.log(` CSS: tokens.css + treatments.css + global.css`);
2738
+ console.log(` @layer cascade: ${hasLayers ? GREEN9 + "yes" + RESET9 : YELLOW6 + "missing" + RESET9}`);
2667
2739
  console.log("");
2668
2740
  console.log(`${BOLD4} Ready!${RESET9} Next steps:`);
2669
2741
  console.log(` 1. Read ${cyan("DECANTR.md")} to understand the design system`);
@@ -3576,12 +3648,22 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET13}`);
3576
3648
  const blueprints = blueprintsResult.data.items;
3577
3649
  const themes = themesResult.data.items;
3578
3650
  let options;
3651
+ const userExplicit = {
3652
+ theme: Boolean(args.theme),
3653
+ mode: Boolean(args.mode),
3654
+ shape: Boolean(args.shape),
3655
+ personality: Boolean(args.personality)
3656
+ };
3579
3657
  if (args.yes || selectedBlueprint !== "default") {
3580
3658
  const flags = parseFlags(args, detected);
3581
3659
  flags.blueprint = selectedBlueprint !== "default" ? selectedBlueprint : flags.blueprint;
3582
3660
  options = mergeWithDefaults(flags, detected);
3583
3661
  } else {
3584
3662
  options = await runInteractivePrompts(detected, archetypes, blueprints, themes);
3663
+ userExplicit.theme = true;
3664
+ userExplicit.mode = true;
3665
+ userExplicit.shape = true;
3666
+ userExplicit.personality = true;
3585
3667
  }
3586
3668
  let topologyMarkdown = "";
3587
3669
  let archetypeData;
@@ -3592,20 +3674,19 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET13}`);
3592
3674
  if (options.blueprint) {
3593
3675
  const blueprintResult = await registryClient.fetchBlueprint(options.blueprint);
3594
3676
  if (blueprintResult) {
3595
- const rawBlueprint = blueprintResult.data;
3596
- const blueprint = rawBlueprint.data ?? rawBlueprint;
3677
+ const blueprint = blueprintResult.data;
3597
3678
  if (blueprint.theme) {
3598
- if ((blueprint.theme.id || blueprint.theme.style) && options.theme === "luminarum") {
3679
+ if (!userExplicit.theme && (blueprint.theme.id || blueprint.theme.style)) {
3599
3680
  options.theme = blueprint.theme.id || blueprint.theme.style;
3600
3681
  }
3601
- if (blueprint.theme.mode && options.mode === "dark") {
3682
+ if (!userExplicit.mode && blueprint.theme.mode) {
3602
3683
  options.mode = blueprint.theme.mode;
3603
3684
  }
3604
- if (blueprint.theme.shape && options.shape === "rounded") {
3685
+ if (!userExplicit.shape && blueprint.theme.shape) {
3605
3686
  options.shape = blueprint.theme.shape;
3606
3687
  }
3607
3688
  }
3608
- if (blueprint.personality && (!options.personality || options.personality.length === 0 || options.personality.length === 1 && options.personality[0] === "professional")) {
3689
+ if (!userExplicit.personality && blueprint.personality) {
3609
3690
  options.personality = typeof blueprint.personality === "string" ? [blueprint.personality] : blueprint.personality;
3610
3691
  }
3611
3692
  if (blueprint.compose && blueprint.compose.length > 0) {
@@ -3614,9 +3695,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET13}`);
3614
3695
  for (const entry of entries) {
3615
3696
  const id = typeof entry === "string" ? entry : entry.archetype;
3616
3697
  const r = await registryClient.fetchArchetype(id);
3617
- const raw = r?.data;
3618
- const inner = raw?.data ?? raw;
3619
- results.push([id, inner]);
3698
+ results.push([id, r?.data ?? null]);
3620
3699
  }
3621
3700
  const archetypeMap = new Map(results.map(([id, data]) => [id, data || null]));
3622
3701
  const composed = composeArchetypes(entries, archetypeMap);
@@ -3664,8 +3743,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET13}`);
3664
3743
  try {
3665
3744
  const result2 = await registryClient.fetchPattern(pid);
3666
3745
  if (result2) {
3667
- const raw = result2.data;
3668
- const inner = raw.data ?? raw;
3746
+ const inner = result2.data;
3669
3747
  const defaultPreset = inner.default_preset || "standard";
3670
3748
  const preset = inner.presets?.[defaultPreset];
3671
3749
  patternSpecs[pid] = {
@@ -3717,8 +3795,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET13}`);
3717
3795
  } else if (options.archetype) {
3718
3796
  const archetypeResult = await registryClient.fetchArchetype(options.archetype);
3719
3797
  if (archetypeResult) {
3720
- const rawArch = archetypeResult.data;
3721
- archetypeData = rawArch.data ?? rawArch;
3798
+ archetypeData = archetypeResult.data;
3722
3799
  } else {
3723
3800
  console.log(`${YELLOW9} Warning: Could not fetch archetype "${options.archetype}". Using defaults.${RESET13}`);
3724
3801
  }
@@ -3727,8 +3804,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET13}`);
3727
3804
  if (options.theme) {
3728
3805
  const themeResult = await registryClient.fetchTheme(options.theme);
3729
3806
  if (themeResult) {
3730
- const rawTheme = themeResult.data;
3731
- const theme = rawTheme.data ?? rawTheme;
3807
+ const theme = themeResult.data;
3732
3808
  themeData = {
3733
3809
  seed: theme.seed,
3734
3810
  palette: theme.palette,
@@ -4256,7 +4332,7 @@ async function main() {
4256
4332
  break;
4257
4333
  }
4258
4334
  case "upgrade": {
4259
- const { cmdUpgrade } = await import("./upgrade-T3FNFFIH.js");
4335
+ const { cmdUpgrade } = await import("./upgrade-PRABSTXX.js");
4260
4336
  const applyFlag = args.includes("--apply");
4261
4337
  await cmdUpgrade(process.cwd(), { apply: applyFlag });
4262
4338
  break;
@@ -9,6 +9,8 @@ function generateTreatmentCSS(spatialTokens, treatmentOverrides, themeDecorators
9
9
  const lines = [];
10
10
  lines.push("/* Generated by @decantr/cli \u2014 Visual Treatment System */");
11
11
  lines.push("");
12
+ lines.push("@layer treatments {");
13
+ lines.push("");
12
14
  lines.push("/* \u2500\u2500 Layer 1: Base Treatments \u2500\u2500 */");
13
15
  lines.push("");
14
16
  function emitRule(selector, props) {
@@ -202,16 +204,6 @@ function generateTreatmentCSS(spatialTokens, treatmentOverrides, themeDecorators
202
204
  ["color", "var(--d-text-muted)"],
203
205
  ["font-family", "var(--d-font-mono, ui-monospace, monospace)"]
204
206
  ]);
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 */`);
208
- lines.push("");
209
- for (const [name, description] of Object.entries(themeDecorators)) {
210
- const rule = generateDecoratorRule(name, description);
211
- lines.push(rule);
212
- lines.push("");
213
- }
214
- }
215
207
  lines.push("/* \u2500\u2500 Keyframes \u2500\u2500 */");
216
208
  lines.push("");
217
209
  lines.push("@keyframes decantr-fade-in {");
@@ -223,6 +215,22 @@ function generateTreatmentCSS(spatialTokens, treatmentOverrides, themeDecorators
223
215
  lines.push(" 0%, 100% { opacity: 1; }");
224
216
  lines.push(" 50% { opacity: 0.5; }");
225
217
  lines.push("}");
218
+ lines.push("");
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
+ }
226
234
  return lines.join("\n");
227
235
  }
228
236
  function generatePersonalityCSS(personality, themeData) {
@@ -252,7 +260,7 @@ function generatePersonalityCSS(personality, themeData) {
252
260
  rules.push(`@keyframes decantr-entrance { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }`);
253
261
  }
254
262
  if (rules.length === 0) return "";
255
- return "\n/* \u2500\u2500 Personality-Derived Utilities \u2500\u2500 */\n\n" + rules.join("\n\n") + "\n";
263
+ return "\n@layer utilities {\n\n/* \u2500\u2500 Personality-Derived Utilities \u2500\u2500 */\n\n" + rules.join("\n\n") + "\n\n} /* end @layer utilities */\n";
256
264
  }
257
265
 
258
266
  // src/scaffold.ts
@@ -509,6 +517,7 @@ function generateTokensCSS(themeData, mode, spatialTokens) {
509
517
  if (!themeData) {
510
518
  const spatialLines2 = spatialTokens ? "\n" + Object.entries(spatialTokens).map(([k, v]) => ` ${k}: ${v};`).join("\n") : "";
511
519
  return `/* No theme data available */
520
+ @layer tokens {
512
521
  :root {
513
522
  --d-primary: #6366f1;
514
523
  --d-secondary: #a1a1aa;
@@ -520,6 +529,7 @@ function generateTokensCSS(themeData, mode, spatialTokens) {
520
529
  --d-text: #fafafa;
521
530
  --d-text-muted: #a1a1aa;${spatialLines2}
522
531
  }
532
+ }
523
533
  `;
524
534
  }
525
535
  const seed = themeData.seed || {};
@@ -584,6 +594,7 @@ function generateTokensCSS(themeData, mode, spatialTokens) {
584
594
  const lines = Object.entries(tokens).map(([key, value]) => ` ${key}: ${value};`).join("\n");
585
595
  const spatialLines = spatialTokens ? "\n" + Object.entries(spatialTokens).map(([k, v]) => ` ${k}: ${v};`).join("\n") : "";
586
596
  let css = `/* Generated by @decantr/cli */
597
+ @layer tokens {
587
598
  :root {
588
599
  ${lines}${spatialLines}
589
600
  }
@@ -612,9 +623,11 @@ ${lightLines}
612
623
  }
613
624
  `;
614
625
  }
626
+ css += `}
627
+ `;
615
628
  return css;
616
629
  }
617
- function generateGlobalCSS(personality) {
630
+ function generateGlobalCSS(personality, essence) {
618
631
  const personalityText = personality.join(" ").toLowerCase();
619
632
  let fontBody = "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
620
633
  if (personalityText.includes("inter")) {
@@ -622,7 +635,12 @@ function generateGlobalCSS(personality) {
622
635
  } else if (personalityText.includes("geist")) {
623
636
  fontBody = `'Geist', ${fontBody}`;
624
637
  }
638
+ const mode = essence?.dna?.theme?.mode || "dark";
639
+ const colorScheme = mode === "auto" ? "light dark" : mode;
625
640
  return `/* Generated by @decantr/cli \u2014 global reset + body styles */
641
+ @layer reset, tokens, treatments, decorators, utilities, app;
642
+
643
+ @layer reset {
626
644
 
627
645
  *, *::before, *::after {
628
646
  box-sizing: border-box;
@@ -631,7 +649,7 @@ function generateGlobalCSS(personality) {
631
649
  }
632
650
 
633
651
  html {
634
- color-scheme: dark;
652
+ color-scheme: ${colorScheme};
635
653
  -webkit-font-smoothing: antialiased;
636
654
  -moz-osx-font-smoothing: grayscale;
637
655
  text-rendering: optimizeLegibility;
@@ -671,6 +689,8 @@ input, button, textarea, select {
671
689
  white-space: nowrap;
672
690
  border-width: 0;
673
691
  }
692
+
693
+ }
674
694
  `;
675
695
  }
676
696
  function generateDecoratorRule(name, description) {
@@ -1081,22 +1101,173 @@ Atoms + treatment + theme decorator:
1081
1101
  - **Theme decorators:** \`carbon-glass\`, \`carbon-code\` \u2014 theme-specific decoration from treatments.css
1082
1102
  - **Combined:** \`css('_flex _col') + ' d-surface carbon-card'\`
1083
1103
 
1084
- ### Atoms Quick Reference
1104
+ ### Atom Reference
1105
+
1106
+ #### Display
1107
+ | Atom | CSS |
1108
+ |------|-----|
1109
+ | \`_flex\` | \`display:flex\` |
1110
+ | \`_grid\` | \`display:grid\` |
1111
+ | \`_block\` | \`display:block\` |
1112
+ | \`_inline\` | \`display:inline\` |
1113
+ | \`_inlineflex\` | \`display:inline-flex\` |
1114
+ | \`_none\` | \`display:none\` |
1115
+ | \`_contents\` | \`display:contents\` |
1116
+
1117
+ #### Flexbox
1118
+ | Atom | CSS |
1119
+ |------|-----|
1120
+ | \`_col\` | \`flex-direction:column\` |
1121
+ | \`_row\` | \`flex-direction:row\` |
1122
+ | \`_colrev\` | \`flex-direction:column-reverse\` |
1123
+ | \`_wrap\` | \`flex-wrap:wrap\` |
1124
+ | \`_nowrap\` | \`flex-wrap:nowrap\` |
1125
+ | \`_flex1\` | \`flex:1\` |
1126
+ | \`_flex0\` | \`flex:none\` |
1127
+ | \`_flexauto\` | \`flex:auto\` |
1128
+ | \`_grow\` | \`flex-grow:1\` |
1129
+ | \`_grow0\` | \`flex-grow:0\` |
1130
+ | \`_shrink0\` | \`flex-shrink:0\` |
1131
+
1132
+ #### Alignment
1133
+ | Atom | CSS |
1134
+ |------|-----|
1135
+ | \`_aic\` | \`align-items:center\` |
1136
+ | \`_aifs\` | \`align-items:flex-start\` |
1137
+ | \`_aife\` | \`align-items:flex-end\` |
1138
+ | \`_aist\` | \`align-items:stretch\` |
1139
+ | \`_aibl\` | \`align-items:baseline\` |
1140
+ | \`_jcc\` | \`justify-content:center\` |
1141
+ | \`_jcfs\` | \`justify-content:flex-start\` |
1142
+ | \`_jcfe\` | \`justify-content:flex-end\` |
1143
+ | \`_jcsb\` | \`justify-content:space-between\` |
1144
+ | \`_jcsa\` | \`justify-content:space-around\` |
1145
+ | \`_jcse\` | \`justify-content:space-evenly\` |
1146
+ | \`_pic\` | \`place-items:center\` |
1147
+ | \`_pcc\` | \`place-content:center\` |
1148
+
1149
+ #### Spacing (scale: 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, ...)
1150
+ | Atom | CSS | Notes |
1151
+ |------|-----|-------|
1152
+ | \`_gap{n}\` | \`gap:{scale}\` | e.g. \`_gap4\` = \`gap:1rem\` |
1153
+ | \`_gx{n}\` | \`column-gap:{scale}\` | horizontal gap |
1154
+ | \`_gy{n}\` | \`row-gap:{scale}\` | vertical gap |
1155
+ | \`_p{n}\` | \`padding:{scale}\` | all sides |
1156
+ | \`_pt{n}\`, \`_pr{n}\`, \`_pb{n}\`, \`_pl{n}\` | directional padding | top/right/bottom/left |
1157
+ | \`_px{n}\` | \`padding-inline:{scale}\` | horizontal |
1158
+ | \`_py{n}\` | \`padding-block:{scale}\` | vertical |
1159
+ | \`_m{n}\` | \`margin:{scale}\` | same as padding variants |
1160
+ | \`_mx{n}\`, \`_my{n}\` | inline/block margin | horizontal/vertical |
1085
1161
 
1086
- | Category | Examples | Purpose |
1087
- |----------|---------|---------|
1088
- | Layout | \`_flex\`, \`_col\`, \`_row\`, \`_wrap\`, \`_grid\` | Flex/grid containers |
1089
- | Spacing | \`_gap4\`, \`_p4\`, \`_px4\`, \`_py2\`, \`_m0\` | Gaps, padding, margin |
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 |
1093
- | Alignment | \`_aic\`, \`_jcc\`, \`_jcsb\`, \`_pic\` | Flex/grid alignment |
1094
- | Position | \`_rel\`, \`_abs\`, \`_sticky\`, \`_z10\` | Positioning |
1095
- | Visual | \`_rounded\`, \`_shadow\`, \`_trans\`, \`_op50\` | Decoration |
1096
- | Responsive | \`_md:gc2\`, \`_lg:gc4\`, \`_sm:flex\` | Breakpoint prefixes |
1097
- | Cursor | \`_pointer\` | Interaction hints |
1162
+ #### Sizing
1163
+ | Atom | CSS |
1164
+ |------|-----|
1165
+ | \`_wfull\` / \`_w100\` | \`width:100%\` |
1166
+ | \`_hfull\` / \`_h100\` | \`height:100%\` |
1167
+ | \`_wscreen\` | \`width:100vw\` |
1168
+ | \`_hscreen\` | \`height:100vh\` |
1169
+ | \`_wfit\` | \`width:fit-content\` |
1170
+ | \`_hfit\` | \`height:fit-content\` |
1171
+ | \`_wauto\` | \`width:auto\` |
1172
+ | \`_minw0\` | \`min-width:0\` |
1173
+ | \`_minh0\` | \`min-height:0\` |
1174
+ | \`_w{n}\`, \`_h{n}\` | width/height from spacing scale |
1175
+ | \`_minw{n}\`, \`_maxw{n}\` | min/max width from scale |
1098
1176
 
1099
- Scale: 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24. Example: \`_gap4\` = \`gap:1rem\`.
1177
+ #### Text Size
1178
+ | Atom | Size | Line-height |
1179
+ |------|------|-------------|
1180
+ | \`_textxs\` | 0.75rem | 1rem |
1181
+ | \`_textsm\` | 0.875rem | 1.25rem |
1182
+ | \`_textbase\` | 1rem | 1.5rem |
1183
+ | \`_textlg\` | 1.125rem | 1.75rem |
1184
+ | \`_textxl\` | 1.25rem | 1.75rem |
1185
+ | \`_text2xl\` | 1.5rem | 2rem |
1186
+ | \`_text3xl\` | 1.875rem | 2.25rem |
1187
+ | \`_heading1\`-\`_heading6\` | Heading presets (size + weight) |
1188
+
1189
+ #### Text Style
1190
+ | Atom | CSS |
1191
+ |------|-----|
1192
+ | \`_fontbold\` | \`font-weight:700\` |
1193
+ | \`_fontsemi\` | \`font-weight:600\` |
1194
+ | \`_fontmedium\` | \`font-weight:500\` |
1195
+ | \`_fontlight\` | \`font-weight:300\` |
1196
+ | \`_italic\` | \`font-style:italic\` |
1197
+ | \`_underline\` | \`text-decoration:underline\` |
1198
+ | \`_uppercase\` | \`text-transform:uppercase\` |
1199
+ | \`_truncate\` | overflow ellipsis + nowrap |
1200
+ | \`_textl\`, \`_textc\`, \`_textr\` | text-align left/center/right |
1201
+
1202
+ #### Color (theme variable based)
1203
+ | Atom | CSS |
1204
+ |------|-----|
1205
+ | \`_bgprimary\` | \`background:var(--d-primary)\` |
1206
+ | \`_bgsurface\` | \`background:var(--d-surface)\` |
1207
+ | \`_bgsurface0\`-\`_bgsurface2\` | surface elevation layers |
1208
+ | \`_bgmuted\` | \`background:var(--d-muted)\` |
1209
+ | \`_bgbg\` | \`background:var(--d-bg)\` |
1210
+ | \`_bgsuccess\`, \`_bgerror\`, \`_bgwarning\`, \`_bginfo\` | status backgrounds |
1211
+ | \`_fgprimary\` | \`color:var(--d-primary)\` |
1212
+ | \`_fgtext\` | \`color:var(--d-text)\` |
1213
+ | \`_fgmuted\` | \`color:var(--d-text-muted)\` |
1214
+ | \`_fgsuccess\`, \`_fgerror\`, \`_fgwarning\`, \`_fginfo\` | status text |
1215
+ | \`_bcborder\` | \`border-color:var(--d-border)\` |
1216
+
1217
+ #### Overflow & Whitespace
1218
+ | Atom | CSS |
1219
+ |------|-----|
1220
+ | \`_overhidden\` | \`overflow:hidden\` |
1221
+ | \`_overauto\` | \`overflow:auto\` |
1222
+ | \`_overscroll\` | \`overflow:scroll\` |
1223
+ | \`_overxauto\`, \`_overyauto\` | axis-specific overflow |
1224
+ | \`_nowraptext\` | \`white-space:nowrap\` |
1225
+ | \`_prewrap\` | \`white-space:pre-wrap\` |
1226
+ | \`_breakword\` | \`overflow-wrap:break-word\` |
1227
+
1228
+ #### Cursor & Interaction
1229
+ | Atom | CSS |
1230
+ |------|-----|
1231
+ | \`_pointer\` | \`cursor:pointer\` |
1232
+ | \`_cursordefault\` | \`cursor:default\` |
1233
+ | \`_notallowed\` | \`cursor:not-allowed\` |
1234
+ | \`_grab\` | \`cursor:grab\` |
1235
+ | \`_selectnone\` | \`user-select:none\` |
1236
+ | \`_ptrnone\` | \`pointer-events:none\` |
1237
+
1238
+ #### Position & Layout
1239
+ | Atom | CSS |
1240
+ |------|-----|
1241
+ | \`_rel\` | \`position:relative\` |
1242
+ | \`_abs\` | \`position:absolute\` |
1243
+ | \`_fixed\` | \`position:fixed\` |
1244
+ | \`_sticky\` | \`position:sticky\` |
1245
+ | \`_inset0\` | \`inset:0\` |
1246
+ | \`_top0\`, \`_right0\`, \`_bottom0\`, \`_left0\` | edge positioning |
1247
+ | \`_z10\`-\`_z50\` | z-index scale |
1248
+
1249
+ #### Grid
1250
+ | Atom | CSS |
1251
+ |------|-----|
1252
+ | \`_gc1\`-\`_gc12\` | \`grid-template-columns:repeat(N,...)\` |
1253
+ | \`_gr1\`-\`_gr6\` | \`grid-template-rows:repeat(N,...)\` |
1254
+ | \`_span1\`-\`_span12\`, \`_spanfull\` | column span |
1255
+ | \`_rowspan1\`-\`_rowspan6\` | row span |
1256
+
1257
+ #### Visual
1258
+ | Atom | CSS |
1259
+ |------|-----|
1260
+ | \`_rounded\` | \`border-radius:var(--d-radius)\` |
1261
+ | \`_roundedfull\` | \`border-radius:9999px\` |
1262
+ | \`_roundedsm\`, \`_roundedlg\`, \`_roundedxl\` | radius variants |
1263
+ | \`_shadow\`, \`_shadowmd\`, \`_shadowlg\` | box-shadow presets |
1264
+ | \`_bordernone\` | \`border:none\` |
1265
+ | \`_bw{n}\` | \`border-width:{n}px\` |
1266
+ | \`_op0\`-\`_op100\` | opacity (0, 25, 50, 75, 100) |
1267
+ | \`_trans\` | \`transition:all 0.15s ease\` |
1268
+ | \`_visible\`, \`_invisible\` | visibility |
1269
+
1270
+ Responsive prefixes: \`_sm:\`, \`_md:\`, \`_lg:\` (e.g. \`_md:gc2\`, \`_lg:gc4\`, \`_sm:flex\`).
1100
1271
 
1101
1272
  ### Section Labels
1102
1273
 
@@ -1140,12 +1311,44 @@ Check \`decantr.essence.json\` \u2192 \`meta.platform.routing\` for the routing
1140
1311
  - \`"history"\` \u2192 use \`BrowserRouter\` (e.g., for server-rendered apps)
1141
1312
 
1142
1313
  Routes are defined in \`decantr.essence.json\` \u2192 \`blueprint.routes\` and listed in \`.decantr/context/scaffold.md\`.`;
1143
- function generateDecantrMdV31(guardMode, cssApproach) {
1314
+ function generateDecantrMdV31(params) {
1144
1315
  const template = loadTemplate("DECANTR.md.template");
1145
- return renderTemplate(template, {
1146
- GUARD_MODE: guardMode,
1147
- CSS_APPROACH: cssApproach
1316
+ const body = renderTemplate(template, {
1317
+ GUARD_MODE: params.guardMode,
1318
+ CSS_APPROACH: params.cssApproach
1148
1319
  });
1320
+ const briefLines = [];
1321
+ briefLines.push("## Project Brief");
1322
+ briefLines.push("");
1323
+ 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 ? ")" : "")}`);
1328
+ if (params.personality && params.personality.length > 0) {
1329
+ briefLines.push(`- **Personality:** ${params.personality.join(". ")}`);
1330
+ }
1331
+ if (params.sections && params.sections.length > 0) {
1332
+ const sectionList = params.sections.map((s) => `${s.id} [${s.role}]`).join(", ");
1333
+ briefLines.push(`- **Sections:** ${params.sections.length} (${sectionList})`);
1334
+ }
1335
+ if (params.features && params.features.length > 0) {
1336
+ briefLines.push(`- **Features:** ${params.features.join(", ")}`);
1337
+ }
1338
+ briefLines.push(`- **Guard mode:** ${params.guardMode}`);
1339
+ briefLines.push("");
1340
+ if (params.decorators && params.decorators.length > 0) {
1341
+ briefLines.push("### Decorator Quick Reference");
1342
+ briefLines.push("| Class | Purpose |");
1343
+ briefLines.push("|-------|---------|");
1344
+ for (const d of params.decorators) {
1345
+ briefLines.push(`| \`.${d.name}\` | ${d.description} |`);
1346
+ }
1347
+ briefLines.push("");
1348
+ }
1349
+ briefLines.push("---");
1350
+ briefLines.push("");
1351
+ return briefLines.join("\n") + body;
1149
1352
  }
1150
1353
  function generateProjectJson(detected, options, registrySource) {
1151
1354
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -1555,8 +1758,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1555
1758
  if (!themeData) try {
1556
1759
  const themeResult = await registry.fetchTheme(themeName);
1557
1760
  if (themeResult?.data) {
1558
- const raw = themeResult.data;
1559
- const t = raw.data ?? raw;
1761
+ const t = themeResult.data;
1560
1762
  themeData = {
1561
1763
  seed: t.seed,
1562
1764
  palette: t.palette,
@@ -1607,7 +1809,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1607
1809
  }
1608
1810
  const stylesDir = join(projectRoot, "src", "styles");
1609
1811
  mkdirSync(stylesDir, { recursive: true });
1610
- const densityLevel = options.density || "comfortable";
1812
+ const densityLevel = essence.dna?.spacing?.density || "comfortable";
1611
1813
  const spatialTokens = computeSpatialTokens(densityLevel, themeData?.spatial ? {
1612
1814
  section_padding: themeData.spatial.section_padding ?? void 0,
1613
1815
  density_bias: typeof themeData.spatial.density_bias === "number" ? themeData.spatial.density_bias : void 0,
@@ -1630,11 +1832,45 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1630
1832
  writeFileSync(treatmentsPath, treatmentCSS);
1631
1833
  const globalPath = join(stylesDir, "global.css");
1632
1834
  if (!existsSync(globalPath)) {
1633
- writeFileSync(globalPath, generateGlobalCSS(personality));
1835
+ writeFileSync(globalPath, generateGlobalCSS(personality, essence));
1634
1836
  }
1635
1837
  const cssFiles = [tokensPath, treatmentsPath, globalPath];
1838
+ const earlyDecoratorList = [];
1839
+ if (themeData?.decorators) {
1840
+ for (const [name, desc] of Object.entries(themeData.decorators)) {
1841
+ earlyDecoratorList.push({ name, description: desc });
1842
+ }
1843
+ }
1844
+ const allFeatures = [];
1845
+ const sectionSummaries = [];
1846
+ if (essence.blueprint.sections && essence.blueprint.sections.length > 0) {
1847
+ for (const s of essence.blueprint.sections) {
1848
+ sectionSummaries.push({ id: s.id, role: s.role });
1849
+ if (s.features) {
1850
+ for (const f of s.features) {
1851
+ if (!allFeatures.includes(f)) allFeatures.push(f);
1852
+ }
1853
+ }
1854
+ }
1855
+ }
1856
+ if (essence.blueprint.features) {
1857
+ for (const f of essence.blueprint.features) {
1858
+ if (!allFeatures.includes(f)) allFeatures.push(f);
1859
+ }
1860
+ }
1636
1861
  const decantrMdPath = join(projectRoot, "DECANTR.md");
1637
- writeFileSync(decantrMdPath, generateDecantrMdV31(guardMode, CSS_APPROACH_CONTENT));
1862
+ writeFileSync(decantrMdPath, generateDecantrMdV31({
1863
+ guardMode,
1864
+ cssApproach: CSS_APPROACH_CONTENT,
1865
+ blueprintId: essence.meta.blueprint || void 0,
1866
+ themeName,
1867
+ themeMode: mode,
1868
+ themeShape: essence.dna.theme.shape || void 0,
1869
+ personality,
1870
+ sections: sectionSummaries.length > 0 ? sectionSummaries : void 0,
1871
+ features: allFeatures.length > 0 ? allFeatures : void 0,
1872
+ decorators: earlyDecoratorList.length > 0 ? earlyDecoratorList : void 0
1873
+ }));
1638
1874
  const hasSections = essence.blueprint.sections && essence.blueprint.sections.length > 0;
1639
1875
  const contextFiles = [];
1640
1876
  if (!hasSections) {
@@ -1671,8 +1907,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1671
1907
  try {
1672
1908
  const patResult = await registry.fetchPattern(name);
1673
1909
  if (patResult?.data) {
1674
- const raw = patResult.data;
1675
- const inner = raw.data ?? raw;
1910
+ const inner = patResult.data;
1676
1911
  const defaultPreset = inner.default_preset || "standard";
1677
1912
  const preset = inner.presets?.[defaultPreset];
1678
1913
  let slots = preset?.layout?.slots || {};
@@ -1685,7 +1920,13 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1685
1920
  const spec = {
1686
1921
  description: inner.description || "",
1687
1922
  components: inner.components || [],
1688
- slots
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
1689
1930
  };
1690
1931
  if (!spec.components || spec.components.length === 0) {
1691
1932
  const syntheticComps = generateSyntheticComponents(name, spec.description);
@@ -1738,19 +1979,6 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1738
1979
  for (const [name, desc] of Object.entries(themeData.decorators)) {
1739
1980
  decoratorList.push({ name, description: desc });
1740
1981
  }
1741
- } else if (existsSync(decoratorsPath)) {
1742
- const decoratorsCss = readFileSync(decoratorsPath, "utf-8");
1743
- const classRegex = /\/\*\s*(.+?)\s*\*\/\s*\n\s*\.([\w-]+)\s*\{/g;
1744
- let match;
1745
- while ((match = classRegex.exec(decoratorsCss)) !== null) {
1746
- decoratorList.push({ name: match[2], description: match[1] });
1747
- }
1748
- if (decoratorList.length === 0) {
1749
- const simpleClassRegex = /^\.([\w-]+)\s*\{/gm;
1750
- while ((match = simpleClassRegex.exec(decoratorsCss)) !== null) {
1751
- decoratorList.push({ name: match[1], description: "" });
1752
- }
1753
- }
1754
1982
  }
1755
1983
  const shellInfoCache = {};
1756
1984
  const seenShells = /* @__PURE__ */ new Set();
@@ -1761,8 +1989,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1761
1989
  try {
1762
1990
  const shellResult = await registry.fetchShell(shellId);
1763
1991
  if (shellResult?.data) {
1764
- const raw = shellResult.data;
1765
- const inner = raw.data ?? raw;
1992
+ const inner = shellResult.data;
1766
1993
  shellInfoCache[shellId] = {
1767
1994
  description: inner.description || "",
1768
1995
  regions: inner.config?.regions || [],
@@ -1806,8 +2033,15 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1806
2033
  themeName,
1807
2034
  zoneContext,
1808
2035
  patternSpecs: sectionPatterns,
2036
+ themeHints: themeData ? {
2037
+ preferred: themeData.pattern_preferences?.prefer,
2038
+ compositions: themeData.compositions ? Object.entries(themeData.compositions).map(([k, v]) => `**${k}:** ${v.description || v}`).join("\n") : void 0,
2039
+ spatialHints: themeData.spatial ? `Density bias: ${themeData.spatial.density_bias || "none"}. Section padding: ${themeData.spatial.section_padding || "default"}. Card wrapping: ${themeData.spatial.card_wrapping || "default"}.` : void 0
2040
+ } : void 0,
1809
2041
  constraints: essence.dna.constraints,
1810
- shellInfo: shellInfoCache[section.shell]
2042
+ shellInfo: shellInfoCache[section.shell],
2043
+ themeData,
2044
+ themeMode: mode
1811
2045
  });
1812
2046
  const sectionContextPath = join(contextDir, `section-${section.id}.md`);
1813
2047
  writeFileSync(sectionContextPath, contextContent);
@@ -1851,8 +2085,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1851
2085
  try {
1852
2086
  const patResult = await registry.fetchPattern(name);
1853
2087
  if (patResult?.data) {
1854
- const raw = patResult.data;
1855
- const inner = raw.data ?? raw;
2088
+ const inner = patResult.data;
1856
2089
  const defaultPreset = inner.default_preset || "standard";
1857
2090
  const preset = inner.presets?.[defaultPreset];
1858
2091
  let slots = preset?.layout?.slots || {};
@@ -1865,7 +2098,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1865
2098
  const spec = {
1866
2099
  description: inner.description || "",
1867
2100
  components: inner.components || [],
1868
- slots
2101
+ slots,
2102
+ layout_hints: inner.layout_hints
1869
2103
  };
1870
2104
  if (!spec.components || spec.components.length === 0) {
1871
2105
  const syntheticComps = generateSyntheticComponents(name, spec.description);
@@ -1896,26 +2130,12 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1896
2130
  for (const [name, desc] of Object.entries(themeData.decorators)) {
1897
2131
  decoratorList.push({ name, description: desc });
1898
2132
  }
1899
- } else if (existsSync(decoratorsPath)) {
1900
- const decoratorsCss = readFileSync(decoratorsPath, "utf-8");
1901
- const classRegex = /\/\*\s*(.+?)\s*\*\/\s*\n\s*\.([\w-]+)\s*\{/g;
1902
- let match;
1903
- while ((match = classRegex.exec(decoratorsCss)) !== null) {
1904
- decoratorList.push({ name: match[2], description: match[1] });
1905
- }
1906
- if (decoratorList.length === 0) {
1907
- const simpleClassRegex = /^\.([\w-]+)\s*\{/gm;
1908
- while ((match = simpleClassRegex.exec(decoratorsCss)) !== null) {
1909
- decoratorList.push({ name: match[1], description: "" });
1910
- }
1911
- }
1912
2133
  }
1913
2134
  let v30ShellInfo;
1914
2135
  try {
1915
2136
  const shellResult = await registry.fetchShell(shell);
1916
2137
  if (shellResult?.data) {
1917
- const raw = shellResult.data;
1918
- const inner = raw.data ?? raw;
2138
+ const inner = shellResult.data;
1919
2139
  v30ShellInfo = {
1920
2140
  description: inner.description || "",
1921
2141
  regions: inner.config?.regions || [],
@@ -1934,8 +2154,15 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1934
2154
  themeName,
1935
2155
  zoneContext: `This is the primary section (${shell} shell).`,
1936
2156
  patternSpecs,
2157
+ themeHints: themeData ? {
2158
+ preferred: themeData.pattern_preferences?.prefer,
2159
+ compositions: themeData.compositions ? Object.entries(themeData.compositions).map(([k, v]) => `**${k}:** ${v.description || v}`).join("\n") : void 0,
2160
+ spatialHints: themeData.spatial ? `Density bias: ${themeData.spatial.density_bias || "none"}. Section padding: ${themeData.spatial.section_padding || "default"}. Card wrapping: ${themeData.spatial.card_wrapping || "default"}.` : void 0
2161
+ } : void 0,
1937
2162
  constraints: essence.dna.constraints,
1938
- shellInfo: v30ShellInfo
2163
+ shellInfo: v30ShellInfo,
2164
+ themeData,
2165
+ themeMode: mode
1939
2166
  });
1940
2167
  const sectionContextPath = join(contextDir, `section-${syntheticSection.id}.md`);
1941
2168
  writeFileSync(sectionContextPath, contextContent);
@@ -2067,14 +2294,47 @@ function generateSectionContext(input) {
2067
2294
  lines.push("");
2068
2295
  lines.push(`**Guard:** ${guardConfig.mode} mode | DNA violations = ${guardConfig.dna_enforcement} | Blueprint violations = ${guardConfig.blueprint_enforcement}`);
2069
2296
  lines.push("");
2070
- lines.push(`**Theme tokens:** see \`src/styles/tokens.css\` \u2014 use \`var(--d-primary)\`, \`var(--d-bg)\`, etc.`);
2297
+ lines.push("**Key palette tokens:**");
2298
+ lines.push("");
2299
+ lines.push("| Token | Value | Role |");
2300
+ lines.push("|-------|-------|------|");
2301
+ const semanticRoles = {
2302
+ background: "Page canvas / base layer",
2303
+ surface: "Cards, panels, containers",
2304
+ "surface-raised": "Elevated containers, modals, popovers",
2305
+ border: "Dividers, card borders, separators",
2306
+ text: "Body text, headings, primary content",
2307
+ "text-muted": "Secondary text, placeholders, labels",
2308
+ primary: "Brand color, key interactive, selected states",
2309
+ "primary-hover": "Hover state for primary elements"
2310
+ };
2311
+ if (input.themeData?.palette) {
2312
+ const modeKey = input.themeMode || "dark";
2313
+ for (const [name, values] of Object.entries(input.themeData.palette)) {
2314
+ const val = values[modeKey] || values.dark || values.light || Object.values(values)[0];
2315
+ lines.push(`| \`--d-${name}\` | \`${val}\` | ${semanticRoles[name] || ""} |`);
2316
+ }
2317
+ }
2318
+ if (input.themeData?.seed?.accent) {
2319
+ lines.push(`| \`--d-accent\` | \`${input.themeData.seed.accent}\` | CTAs, links, active states, glow effects |`);
2320
+ }
2321
+ lines.push("");
2322
+ lines.push("Full token set: `src/styles/tokens.css`");
2071
2323
  lines.push("");
2072
2324
  lines.push("**Visual Treatments:** All 6 base treatments available (see DECANTR.md for usage).");
2073
2325
  if (decorators.length > 0) {
2074
- const names = decorators.map((d) => d.name).join(", ");
2075
- lines.push(`**Theme decorators:** ${names}`);
2326
+ lines.push("**Theme decorators:**");
2327
+ lines.push("");
2328
+ lines.push("| Class | Usage |");
2329
+ lines.push("|-------|-------|");
2330
+ for (const d of decorators) {
2331
+ lines.push(`| \`.${d.name}\` | ${d.description} |`);
2332
+ }
2333
+ lines.push("");
2334
+ } else {
2335
+ lines.push("**Theme decorators:** None defined for this theme.");
2336
+ lines.push("");
2076
2337
  }
2077
- lines.push("");
2078
2338
  if (themeHints) {
2079
2339
  if (themeHints.preferred && themeHints.preferred.length > 0) {
2080
2340
  lines.push(`**Preferred:** ${themeHints.preferred.join(", ")}`);
@@ -2087,6 +2347,10 @@ function generateSectionContext(input) {
2087
2347
  }
2088
2348
  lines.push("");
2089
2349
  }
2350
+ const themePrefix = themeName.split("-")[0] || themeName;
2351
+ lines.push("");
2352
+ lines.push(`Usage: \`className={css('_flex _col _gap4') + ' d-surface ${themePrefix}-glass'}\` \u2014 atoms via css(), treatments and theme decorators as plain class strings.`);
2353
+ lines.push("");
2090
2354
  lines.push("---");
2091
2355
  lines.push("");
2092
2356
  if (zoneContext) {
@@ -2103,8 +2367,24 @@ function generateSectionContext(input) {
2103
2367
  lines.push("");
2104
2368
  }
2105
2369
  if (personality.length > 0) {
2106
- lines.push("**Personality:** See scaffold.md for personality and visual direction.");
2370
+ const personalityText = personality.join(". ");
2371
+ lines.push("## Visual Direction");
2107
2372
  lines.push("");
2373
+ lines.push(`**Personality:** ${personalityText}`);
2374
+ lines.push("");
2375
+ const pLower = personalityText.toLowerCase();
2376
+ const utils = [];
2377
+ if (pLower.includes("neon") || pLower.includes("glow"))
2378
+ utils.push("`neon-glow`, `neon-glow-hover`, `neon-text-glow`, `neon-border-glow` \u2014 Apply to elements needing accent emphasis");
2379
+ if (pLower.includes("mono") || pLower.includes("monospace"))
2380
+ utils.push("`mono-data` \u2014 Monospace + tabular-nums for metrics, IDs, timestamps");
2381
+ if (pLower.includes("pulse") || pLower.includes("ring") || pLower.includes("status"))
2382
+ utils.push('`status-ring` with `data-status="active|idle|error|processing"` \u2014 Color-coded status with pulse animation');
2383
+ if (utils.length > 0) {
2384
+ lines.push("**Personality utilities available in treatments.css:**");
2385
+ for (const u of utils) lines.push(`- ${u}`);
2386
+ lines.push("");
2387
+ }
2108
2388
  }
2109
2389
  if (constraints && Object.keys(constraints).length > 0) {
2110
2390
  lines.push("## Constraints");
@@ -2133,12 +2413,59 @@ function generateSectionContext(input) {
2133
2413
  lines.push("");
2134
2414
  lines.push(spec.description);
2135
2415
  lines.push("");
2416
+ if (spec.visual_brief) {
2417
+ lines.push(`**Visual brief:** ${spec.visual_brief}`);
2418
+ lines.push("");
2419
+ }
2136
2420
  lines.push(`**Components:** ${spec.components.join(", ")}`);
2137
2421
  lines.push("");
2422
+ if (spec.composition && Object.keys(spec.composition).length > 0) {
2423
+ lines.push("**Composition:**");
2424
+ lines.push("```");
2425
+ for (const [name, expr] of Object.entries(spec.composition)) {
2426
+ lines.push(`${name} = ${expr}`);
2427
+ }
2428
+ lines.push("```");
2429
+ lines.push("");
2430
+ }
2138
2431
  lines.push("**Layout slots:**");
2139
2432
  for (const [slot, desc] of Object.entries(spec.slots)) {
2140
2433
  lines.push(`- \`${slot}\`: ${desc}`);
2141
2434
  }
2435
+ if (spec.layout_hints && Object.keys(spec.layout_hints).length > 0) {
2436
+ lines.push(` **Layout guidance:**`);
2437
+ for (const [key, value] of Object.entries(spec.layout_hints)) {
2438
+ lines.push(` - ${key}: ${value}`);
2439
+ }
2440
+ }
2441
+ if (spec.motion) {
2442
+ const entries = [];
2443
+ if (spec.motion.micro) for (const [k, v] of Object.entries(spec.motion.micro)) entries.push([k, v]);
2444
+ if (spec.motion.transitions) for (const [k, v] of Object.entries(spec.motion.transitions)) entries.push([k, v]);
2445
+ if (spec.motion.ambient) for (const [k, v] of Object.entries(spec.motion.ambient)) entries.push([k, v]);
2446
+ if (entries.length > 0) {
2447
+ lines.push("**Motion:**");
2448
+ lines.push("| Interaction | Animation |");
2449
+ lines.push("|-------------|-----------|");
2450
+ for (const [k, v] of entries) lines.push(`| ${k} | ${v} |`);
2451
+ lines.push("");
2452
+ }
2453
+ }
2454
+ if (spec.responsive) {
2455
+ lines.push("**Responsive:**");
2456
+ if (spec.responsive.mobile) lines.push(`- **Mobile (<640px):** ${spec.responsive.mobile}`);
2457
+ if (spec.responsive.tablet) lines.push(`- **Tablet (640-1024px):** ${spec.responsive.tablet}`);
2458
+ if (spec.responsive.desktop) lines.push(`- **Desktop (>1024px):** ${spec.responsive.desktop}`);
2459
+ lines.push("");
2460
+ }
2461
+ if (spec.accessibility) {
2462
+ lines.push("**Accessibility:**");
2463
+ if (spec.accessibility.role) lines.push(`- Role: \`${spec.accessibility.role}\``);
2464
+ if (spec.accessibility.keyboard?.length) lines.push(`- Keyboard: ${spec.accessibility.keyboard.join("; ")}`);
2465
+ if (spec.accessibility.announcements?.length) lines.push(`- Announcements: ${spec.accessibility.announcements.join("; ")}`);
2466
+ if (spec.accessibility.focus_management) lines.push(`- Focus: ${spec.accessibility.focus_management}`);
2467
+ lines.push("");
2468
+ }
2142
2469
  lines.push("");
2143
2470
  }
2144
2471
  lines.push("---");
@@ -2177,6 +2504,18 @@ function generateScaffoldContext(input) {
2177
2504
  lines.push(`**Personality:** ${personality.join(", ")}`);
2178
2505
  lines.push("**Guard mode:** creative (no enforcement during initial scaffolding)");
2179
2506
  lines.push("");
2507
+ if (input.voice) {
2508
+ lines.push("## Voice & Copy");
2509
+ lines.push("");
2510
+ if (input.voice.tone) lines.push(`**Tone:** ${input.voice.tone}`);
2511
+ if (input.voice.cta_verbs?.length) lines.push(`**CTA verbs:** ${input.voice.cta_verbs.join(", ")}`);
2512
+ if (input.voice.avoid?.length) lines.push(`**Avoid:** ${input.voice.avoid.join(", ")}`);
2513
+ if (input.voice.empty_states) lines.push(`**Empty states:** ${input.voice.empty_states}`);
2514
+ if (input.voice.errors) lines.push(`**Errors:** ${input.voice.errors}`);
2515
+ if (input.voice.loading) lines.push(`**Loading states:** ${input.voice.loading}`);
2516
+ if (input.voice.metrics_format) lines.push(`**Metrics format:** ${input.voice.metrics_format}`);
2517
+ lines.push("");
2518
+ }
2180
2519
  lines.push("## App Topology");
2181
2520
  lines.push("");
2182
2521
  lines.push(topologyMarkdown);
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import "./chunk-4KKFOXBB.js";
2
- import "./chunk-MU3657R7.js";
1
+ import "./chunk-G76RSQRX.js";
2
+ import "./chunk-RG7F5APP.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  RegistryClient,
3
3
  refreshDerivedFiles
4
- } from "./chunk-MU3657R7.js";
4
+ } from "./chunk-RG7F5APP.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.5.5",
3
+ "version": "1.6.0",
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": {
@@ -22,13 +22,13 @@
22
22
  "publishConfig": {
23
23
  "access": "public"
24
24
  },
25
+ "dependencies": {
26
+ "@decantr/essence-spec": "1.0.0-beta.11",
27
+ "@decantr/registry": "1.0.0-beta.11"
28
+ },
25
29
  "scripts": {
26
30
  "build": "tsup",
27
31
  "test": "vitest run",
28
32
  "test:watch": "vitest"
29
- },
30
- "dependencies": {
31
- "@decantr/essence-spec": "workspace:*",
32
- "@decantr/registry": "workspace:*"
33
33
  }
34
- }
34
+ }