@decantr/cli 1.7.7 → 1.7.8

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,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-OHQXL7EJ.js";
3
- import "./chunk-4D5V6GQU.js";
2
+ import "./chunk-ODJQZXHQ.js";
3
+ import "./chunk-74G2RQDO.js";
4
4
  import "./chunk-KUDAVJOR.js";
@@ -1375,7 +1375,23 @@ function generateDecantrMdV31(params) {
1375
1375
  const template = loadTemplate("DECANTR.md.template");
1376
1376
  const body = renderTemplate(template, {
1377
1377
  GUARD_MODE: params.guardMode,
1378
- CSS_APPROACH: params.cssApproach
1378
+ CSS_APPROACH: params.cssApproach,
1379
+ WORKFLOW_MODE: params.workflowMode === "brownfield-attach" ? "brownfield attach" : "greenfield scaffold",
1380
+ WORKFLOW_GUIDANCE: params.workflowMode === "brownfield-attach" ? `This project is using Decantr in **brownfield attach** mode.
1381
+
1382
+ Read \`.decantr/analysis.json\` first for the detected framework, routes, styling, layout, and dependency facts.
1383
+ Then read \`.decantr/init-seed.json\` for the recommended attach defaults.
1384
+ Then read \`.decantr/context/scaffold-pack.md\` and \`.decantr/context/scaffold.md\` to understand the Decantr contract you are layering onto the existing app.
1385
+
1386
+ Preserve the current framework, package manager, router, and working runtime structure unless the contract gives you a reviewed reason to change them. Map existing routes and components onto the declared Decantr sections/pages before creating new files. Registry content is optional in this workflow unless the task explicitly asks for it.` : `This project is using Decantr in **greenfield scaffold** mode.
1387
+
1388
+ Treat the compiled execution-pack files as the primary source of truth.
1389
+ Use narrative docs only as secondary explanation when the compiled packs are not enough.
1390
+ Use only files present in this workspace as the source of truth. If local scaffold files disagree, stop and report the mismatch instead of relying on external Decantr assumptions or prior examples.
1391
+
1392
+ Read \`.decantr/context/scaffold-pack.md\` first for the compact compiled shell, theme, feature, and route contract.
1393
+ Then read \`.decantr/context/scaffold.md\` for the fuller app overview, topology, route map, and voice guidance.
1394
+ Start implementation from the shell layouts and shared route structure before filling in section pages.`
1379
1395
  });
1380
1396
  const briefLines = [];
1381
1397
  briefLines.push("## Project Brief");
@@ -1383,6 +1399,7 @@ function generateDecantrMdV31(params) {
1383
1399
  briefLines.push(`- **Blueprint:** ${params.blueprintId || "custom"}`);
1384
1400
  const themeDesc = `${params.themeName || "default"} (${params.themeMode || "dark"} mode${params.themeShape ? `, ${params.themeShape} shape` : ""})`;
1385
1401
  briefLines.push(`- **Theme:** ${themeDesc}`);
1402
+ briefLines.push(`- **Workflow:** ${params.workflowMode === "brownfield-attach" ? "brownfield attach" : "greenfield scaffold"}`);
1386
1403
  if (params.personality && params.personality.length > 0) {
1387
1404
  briefLines.push(`- **Personality:** ${params.personality.join(". ")}`);
1388
1405
  }
@@ -1469,7 +1486,8 @@ function generateProjectJson(detected, options, registrySource) {
1469
1486
  at: now,
1470
1487
  via: "cli",
1471
1488
  version: CLI_VERSION,
1472
- flags: buildFlagsString(options)
1489
+ flags: buildFlagsString(options),
1490
+ workflowMode: options.workflowMode || "greenfield-scaffold"
1473
1491
  }
1474
1492
  };
1475
1493
  if (options.blueprint) {
@@ -1909,7 +1927,8 @@ function scaffoldMinimal(projectRoot) {
1909
1927
  at: now,
1910
1928
  via: "cli",
1911
1929
  version: CLI_VERSION,
1912
- flags: "--offline --minimal"
1930
+ flags: "--offline --minimal",
1931
+ workflowMode: "greenfield-scaffold"
1913
1932
  }
1914
1933
  };
1915
1934
  const projectJsonPath = join(decantrDir, "project.json");
@@ -2109,6 +2128,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
2109
2128
  mkdirSync(contextDir, { recursive: true });
2110
2129
  let storedBlueprintId;
2111
2130
  let storedVoice;
2131
+ let storedWorkflowMode;
2112
2132
  const projectJsonFilePath = join(decantrDir, "project.json");
2113
2133
  let projectJsonData = {};
2114
2134
  if (existsSync(projectJsonFilePath)) {
@@ -2116,6 +2136,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
2116
2136
  projectJsonData = JSON.parse(readFileSync(projectJsonFilePath, "utf-8"));
2117
2137
  if (projectJsonData.blueprintId) storedBlueprintId = projectJsonData.blueprintId;
2118
2138
  if (projectJsonData.voice) storedVoice = projectJsonData.voice;
2139
+ if (projectJsonData.initialized?.workflowMode) storedWorkflowMode = projectJsonData.initialized.workflowMode;
2119
2140
  } catch {
2120
2141
  }
2121
2142
  }
@@ -2234,6 +2255,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
2234
2255
  writeFileSync(decantrMdPath, generateDecantrMdV31({
2235
2256
  guardMode,
2236
2257
  cssApproach: CSS_APPROACH_CONTENT,
2258
+ workflowMode: storedWorkflowMode,
2237
2259
  blueprintId: storedBlueprintId || getLegacyBlueprintId(essence.meta) || void 0,
2238
2260
  themeName,
2239
2261
  themeMode: mode,
@@ -14,13 +14,13 @@ import {
14
14
  scaffoldProject,
15
15
  syncRegistry,
16
16
  writeExecutionPackBundleArtifacts
17
- } from "./chunk-4D5V6GQU.js";
17
+ } from "./chunk-74G2RQDO.js";
18
18
  import {
19
19
  buildGuardRegistryContext
20
20
  } from "./chunk-KUDAVJOR.js";
21
21
 
22
22
  // src/index.ts
23
- import { mkdirSync as mkdirSync10, readFileSync as readFileSync17, writeFileSync as writeFileSync13, existsSync as existsSync25, readdirSync as readdirSync6 } from "fs";
23
+ import { mkdirSync as mkdirSync10, readFileSync as readFileSync18, writeFileSync as writeFileSync13, existsSync as existsSync25, readdirSync as readdirSync6 } from "fs";
24
24
  import { basename as basename2, join as join26, dirname as dirname2, isAbsolute, resolve as resolve3 } from "path";
25
25
  import { fileURLToPath as fileURLToPath2 } from "url";
26
26
  import { validateEssence as validateEssence2, evaluateGuard, isV3 as isV36 } from "@decantr/essence-spec";
@@ -343,7 +343,8 @@ async function runInteractivePrompts(detected, archetypes, blueprints, themes, w
343
343
  shell,
344
344
  personality: ["professional"],
345
345
  features: [],
346
- existing: workflowSeed?.existing || detected.existingEssence
346
+ existing: workflowSeed?.existing || detected.existingEssence,
347
+ workflowMode: workflowSeed?.workflowMode
347
348
  };
348
349
  }
349
350
  function parseFlags(args, detected) {
@@ -375,7 +376,8 @@ function mergeWithDefaults(flags, detected, workflowSeed) {
375
376
  shell: flags.shell || workflowSeed?.shell || "sidebar-main",
376
377
  personality: flags.personality || ["professional"],
377
378
  features: flags.features || [],
378
- existing: flags.existing || workflowSeed?.existing || detected.existingEssence
379
+ existing: flags.existing || workflowSeed?.existing || detected.existingEssence,
380
+ workflowMode: flags.workflowMode || workflowSeed?.workflowMode
379
381
  };
380
382
  }
381
383
  async function runSimplifiedInit(blueprints) {
@@ -1539,7 +1541,7 @@ import { existsSync as existsSync19, mkdirSync as mkdirSync4, writeFileSync as w
1539
1541
  import { join as join19 } from "path";
1540
1542
 
1541
1543
  // src/analyzers/routes.ts
1542
- import { existsSync as existsSync12, readdirSync as readdirSync2, statSync } from "fs";
1544
+ import { existsSync as existsSync12, readFileSync as readFileSync11, readdirSync as readdirSync2, statSync } from "fs";
1543
1545
  import { join as join12, relative } from "path";
1544
1546
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".next", ".git", "api", "_app", "_document"]);
1545
1547
  function shouldSkipDir(name) {
@@ -1630,6 +1632,75 @@ function walkPagesDir(dir, baseDir, segments) {
1630
1632
  }
1631
1633
  return routes;
1632
1634
  }
1635
+ var ROUTER_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".tsx", ".ts", ".jsx", ".js"]);
1636
+ function collectRouteCandidateFiles(dir, files, depth = 0) {
1637
+ if (depth > 5) return;
1638
+ let entries;
1639
+ try {
1640
+ entries = readdirSync2(dir);
1641
+ } catch {
1642
+ return;
1643
+ }
1644
+ for (const entry of entries) {
1645
+ if (entry.startsWith(".") || entry === "node_modules") continue;
1646
+ const fullPath = join12(dir, entry);
1647
+ try {
1648
+ const stat = statSync(fullPath);
1649
+ if (stat.isDirectory()) {
1650
+ collectRouteCandidateFiles(fullPath, files, depth + 1);
1651
+ } else if (stat.isFile()) {
1652
+ const ext = entry.slice(entry.lastIndexOf("."));
1653
+ if (ROUTER_FILE_EXTENSIONS.has(ext)) {
1654
+ files.push(fullPath);
1655
+ }
1656
+ }
1657
+ } catch {
1658
+ continue;
1659
+ }
1660
+ }
1661
+ }
1662
+ function scanReactRouter(projectRoot) {
1663
+ const candidateDirs = [
1664
+ join12(projectRoot, "src"),
1665
+ projectRoot
1666
+ ];
1667
+ const candidateFiles = [];
1668
+ for (const dir of candidateDirs) {
1669
+ if (existsSync12(dir)) collectRouteCandidateFiles(dir, candidateFiles);
1670
+ }
1671
+ const routeMap = /* @__PURE__ */ new Map();
1672
+ for (const absolutePath of candidateFiles) {
1673
+ let content;
1674
+ try {
1675
+ content = readFileSync11(absolutePath, "utf-8");
1676
+ } catch {
1677
+ continue;
1678
+ }
1679
+ const isReactRouterFile = content.includes("react-router-dom") || content.includes("react-router") || content.includes("<Routes") || content.includes("createBrowserRouter") || content.includes("createHashRouter") || content.includes("RouterProvider") || content.includes("HashRouter") || content.includes("BrowserRouter");
1680
+ if (!isReactRouterFile) continue;
1681
+ const relativePath = relative(projectRoot, absolutePath);
1682
+ const pathMatches = /* @__PURE__ */ new Set();
1683
+ for (const match of content.matchAll(/<Route\b[^>]*\bpath=["'`]([^"'`]+)["'`]/g)) {
1684
+ pathMatches.add(match[1]);
1685
+ }
1686
+ for (const match of content.matchAll(/\bpath\s*:\s*["'`]([^"'`]+)["'`]/g)) {
1687
+ pathMatches.add(match[1]);
1688
+ }
1689
+ if (pathMatches.size === 0 && (content.includes("<Routes") || content.includes("RouterProvider"))) {
1690
+ pathMatches.add("/");
1691
+ }
1692
+ for (const path of pathMatches) {
1693
+ if (!routeMap.has(path)) {
1694
+ routeMap.set(path, {
1695
+ path,
1696
+ file: relativePath,
1697
+ hasLayout: false
1698
+ });
1699
+ }
1700
+ }
1701
+ }
1702
+ return [...routeMap.values()];
1703
+ }
1633
1704
  function scanRoutes(projectRoot) {
1634
1705
  const appDirs = [
1635
1706
  join12(projectRoot, "src", "app"),
@@ -1655,6 +1726,10 @@ function scanRoutes(projectRoot) {
1655
1726
  }
1656
1727
  }
1657
1728
  }
1729
+ const reactRouterRoutes = scanReactRouter(projectRoot);
1730
+ if (reactRouterRoutes.length > 0) {
1731
+ return { strategy: "react-router", routes: reactRouterRoutes };
1732
+ }
1658
1733
  return { strategy: "none", routes: [] };
1659
1734
  }
1660
1735
 
@@ -1662,6 +1737,16 @@ function scanRoutes(projectRoot) {
1662
1737
  import { existsSync as existsSync13, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
1663
1738
  import { join as join13 } from "path";
1664
1739
  var PAGE_EXTENSIONS = /* @__PURE__ */ new Set([".tsx", ".ts", ".jsx", ".js"]);
1740
+ var ROOT_COMPONENT_CANDIDATES = [
1741
+ "src/App.tsx",
1742
+ "src/App.ts",
1743
+ "src/App.jsx",
1744
+ "src/App.js",
1745
+ "App.tsx",
1746
+ "App.ts",
1747
+ "App.jsx",
1748
+ "App.js"
1749
+ ];
1665
1750
  function countFilesRecursive(dir, extensions) {
1666
1751
  let count = 0;
1667
1752
  let entries;
@@ -1753,6 +1838,18 @@ function scanComponents(projectRoot) {
1753
1838
  }
1754
1839
  }
1755
1840
  }
1841
+ const hasRootAppComponent = ROOT_COMPONENT_CANDIDATES.some(
1842
+ (relativePath) => existsSync13(join13(projectRoot, relativePath))
1843
+ );
1844
+ if (pageCount === 0 && hasRootAppComponent) {
1845
+ pageCount = 1;
1846
+ }
1847
+ if (componentCount === 0 && hasRootAppComponent) {
1848
+ componentCount = 1;
1849
+ if (!componentDirs.includes("src")) {
1850
+ componentDirs.push("src");
1851
+ }
1852
+ }
1756
1853
  return {
1757
1854
  pageCount,
1758
1855
  componentCount,
@@ -1761,7 +1858,7 @@ function scanComponents(projectRoot) {
1761
1858
  }
1762
1859
 
1763
1860
  // src/analyzers/styling.ts
1764
- import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
1861
+ import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
1765
1862
  import { join as join14 } from "path";
1766
1863
  var TAILWIND_CONFIGS = [
1767
1864
  "tailwind.config.js",
@@ -1772,11 +1869,17 @@ var TAILWIND_CONFIGS = [
1772
1869
  var GLOBALS_CSS_PATHS = [
1773
1870
  "src/app/globals.css",
1774
1871
  "app/globals.css",
1872
+ "src/styles/global.css",
1775
1873
  "src/styles/globals.css",
1776
1874
  "styles/globals.css",
1777
1875
  "src/index.css",
1778
1876
  "src/global.css"
1779
1877
  ];
1878
+ var DECANTR_STYLE_PATHS = [
1879
+ "src/styles/tokens.css",
1880
+ "src/styles/treatments.css",
1881
+ "src/styles/global.css"
1882
+ ];
1780
1883
  function extractCSSVariables(content) {
1781
1884
  const colors = {};
1782
1885
  const variables = [];
@@ -1793,8 +1896,8 @@ function extractCSSVariables(content) {
1793
1896
  }
1794
1897
  return { colors, variables };
1795
1898
  }
1796
- function detectDarkMode(projectRoot, cssContent) {
1797
- if (cssContent) {
1899
+ function detectDarkMode(projectRoot, cssContents) {
1900
+ for (const cssContent of cssContents) {
1798
1901
  if (cssContent.includes(".dark") || cssContent.includes('[data-theme="dark"]') || cssContent.includes("prefers-color-scheme: dark") || cssContent.includes("color-scheme: dark")) {
1799
1902
  return true;
1800
1903
  }
@@ -1809,7 +1912,7 @@ function detectDarkMode(projectRoot, cssContent) {
1809
1912
  const fullPath = join14(projectRoot, rel);
1810
1913
  if (existsSync14(fullPath)) {
1811
1914
  try {
1812
- const layoutContent = readFileSync11(fullPath, "utf-8");
1915
+ const layoutContent = readFileSync12(fullPath, "utf-8");
1813
1916
  if (layoutContent.includes('className="dark"') || layoutContent.includes("className='dark'") || layoutContent.includes('class="dark"')) {
1814
1917
  return true;
1815
1918
  }
@@ -1820,7 +1923,7 @@ function detectDarkMode(projectRoot, cssContent) {
1820
1923
  const pkgPath = join14(projectRoot, "package.json");
1821
1924
  if (existsSync14(pkgPath)) {
1822
1925
  try {
1823
- const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
1926
+ const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
1824
1927
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
1825
1928
  if (allDeps["next-themes"] || allDeps["theme-toggle"] || allDeps["use-dark-mode"]) {
1826
1929
  return true;
@@ -1828,6 +1931,17 @@ function detectDarkMode(projectRoot, cssContent) {
1828
1931
  } catch {
1829
1932
  }
1830
1933
  }
1934
+ const essencePath = join14(projectRoot, "decantr.essence.json");
1935
+ if (existsSync14(essencePath)) {
1936
+ try {
1937
+ const essence = JSON.parse(readFileSync12(essencePath, "utf-8"));
1938
+ const mode = essence.dna?.theme?.mode;
1939
+ if (mode === "dark" || mode === "auto") {
1940
+ return true;
1941
+ }
1942
+ } catch {
1943
+ }
1944
+ }
1831
1945
  return false;
1832
1946
  }
1833
1947
  function scanStyling(projectRoot) {
@@ -1844,8 +1958,12 @@ function scanStyling(projectRoot) {
1844
1958
  const pkgPath = join14(projectRoot, "package.json");
1845
1959
  if (existsSync14(pkgPath)) {
1846
1960
  try {
1847
- const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
1961
+ const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
1848
1962
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
1963
+ if (allDeps["@decantr/css"]) {
1964
+ approach = "decantr-css";
1965
+ configFile = "src/styles/tokens.css";
1966
+ }
1849
1967
  if (allDeps.tailwindcss || allDeps["@tailwindcss/postcss"] || allDeps["@tailwindcss/vite"]) {
1850
1968
  approach = "tailwind";
1851
1969
  }
@@ -1853,25 +1971,44 @@ function scanStyling(projectRoot) {
1853
1971
  }
1854
1972
  }
1855
1973
  }
1856
- let cssContent = null;
1974
+ const decantrStyleFiles = DECANTR_STYLE_PATHS.filter((rel) => existsSync14(join14(projectRoot, rel)));
1975
+ if (decantrStyleFiles.length >= 2) {
1976
+ approach = "decantr-css";
1977
+ configFile = decantrStyleFiles.join(" + ");
1978
+ }
1979
+ const cssContents = [];
1857
1980
  for (const rel of GLOBALS_CSS_PATHS) {
1858
1981
  const fullPath = join14(projectRoot, rel);
1859
1982
  if (existsSync14(fullPath)) {
1860
1983
  try {
1861
- cssContent = readFileSync11(fullPath, "utf-8");
1984
+ cssContents.push(readFileSync12(fullPath, "utf-8"));
1985
+ } catch {
1986
+ }
1987
+ }
1988
+ }
1989
+ for (const rel of DECANTR_STYLE_PATHS) {
1990
+ if (GLOBALS_CSS_PATHS.includes(rel)) continue;
1991
+ const fullPath = join14(projectRoot, rel);
1992
+ if (existsSync14(fullPath)) {
1993
+ try {
1994
+ cssContents.push(readFileSync12(fullPath, "utf-8"));
1862
1995
  } catch {
1863
1996
  }
1864
- break;
1865
1997
  }
1866
1998
  }
1867
1999
  let colors = {};
1868
2000
  let cssVariables = [];
1869
- if (cssContent) {
2001
+ for (const cssContent of cssContents) {
1870
2002
  const extracted = extractCSSVariables(cssContent);
1871
- colors = extracted.colors;
1872
- cssVariables = extracted.variables;
2003
+ colors = { ...colors, ...extracted.colors };
2004
+ cssVariables.push(...extracted.variables);
2005
+ }
2006
+ cssVariables = [...new Set(cssVariables)];
2007
+ const darkMode = detectDarkMode(projectRoot, cssContents);
2008
+ if (approach === "unknown" && cssContents.length > 0) {
2009
+ approach = "css";
2010
+ configFile = GLOBALS_CSS_PATHS.find((rel) => existsSync14(join14(projectRoot, rel)));
1873
2011
  }
1874
- const darkMode = detectDarkMode(projectRoot, cssContent);
1875
2012
  return {
1876
2013
  approach,
1877
2014
  configFile,
@@ -1882,7 +2019,7 @@ function scanStyling(projectRoot) {
1882
2019
  }
1883
2020
 
1884
2021
  // src/analyzers/layout.ts
1885
- import { existsSync as existsSync15, readFileSync as readFileSync12, readdirSync as readdirSync4 } from "fs";
2022
+ import { existsSync as existsSync15, readFileSync as readFileSync13, readdirSync as readdirSync4 } from "fs";
1886
2023
  import { join as join15 } from "path";
1887
2024
  var SIDEBAR_PATTERNS = ["sidebar", "side-bar", "sidenav", "side-nav", "drawer", "aside"];
1888
2025
  var NAV_PATTERNS = ["nav", "navbar", "header", "top-bar", "topbar", "app-bar", "appbar"];
@@ -1927,7 +2064,7 @@ function checkLayoutFiles(projectRoot) {
1927
2064
  if (!existsSync15(layoutPath)) continue;
1928
2065
  let content;
1929
2066
  try {
1930
- content = readFileSync12(layoutPath, "utf-8");
2067
+ content = readFileSync13(layoutPath, "utf-8");
1931
2068
  } catch {
1932
2069
  continue;
1933
2070
  }
@@ -1947,7 +2084,7 @@ function checkLayoutFiles(projectRoot) {
1947
2084
  if (!existsSync15(layoutPath)) continue;
1948
2085
  let content;
1949
2086
  try {
1950
- content = readFileSync12(layoutPath, "utf-8");
2087
+ content = readFileSync13(layoutPath, "utf-8");
1951
2088
  } catch {
1952
2089
  continue;
1953
2090
  }
@@ -1968,17 +2105,40 @@ function inferShellPattern(hasSidebar, hasTopNav, hasFooter) {
1968
2105
  if (hasFooter) return "main-footer";
1969
2106
  return "main-only";
1970
2107
  }
2108
+ function inferShellPatternFromDecantrContract(projectRoot) {
2109
+ const essencePath = join15(projectRoot, "decantr.essence.json");
2110
+ if (!existsSync15(essencePath)) return null;
2111
+ try {
2112
+ const essence = JSON.parse(readFileSync13(essencePath, "utf-8"));
2113
+ const sectionShells = essence.blueprint?.sections?.map((section) => section.shell).filter((shell) => typeof shell === "string" && shell.length > 0) ?? [];
2114
+ if (sectionShells.length === 0 && essence.blueprint?.shell) {
2115
+ return `${essence.blueprint.shell} (contract)`;
2116
+ }
2117
+ const uniqueShells = [...new Set(sectionShells)];
2118
+ if (uniqueShells.length === 0) return null;
2119
+ if (uniqueShells.length === 1) return `${uniqueShells[0]} (contract)`;
2120
+ const primarySection = essence.blueprint?.sections?.find((section) => section.role === "primary" && section.shell);
2121
+ if (primarySection?.shell) {
2122
+ return `${primarySection.shell} (primary contract)`;
2123
+ }
2124
+ return `${uniqueShells[0]} (+${uniqueShells.length - 1} shells, contract)`;
2125
+ } catch {
2126
+ return null;
2127
+ }
2128
+ }
1971
2129
  function scanLayout(projectRoot) {
1972
2130
  const fromComponents = checkComponentDirs(projectRoot);
1973
2131
  const fromLayouts = checkLayoutFiles(projectRoot);
1974
2132
  const hasSidebar = fromComponents.sidebar || fromLayouts.sidebar;
1975
2133
  const hasTopNav = fromComponents.nav || fromLayouts.nav;
1976
2134
  const hasFooter = fromComponents.footer || fromLayouts.footer;
2135
+ const runtimeShellPattern = inferShellPattern(hasSidebar, hasTopNav, hasFooter);
2136
+ const contractShellPattern = inferShellPatternFromDecantrContract(projectRoot);
1977
2137
  return {
1978
2138
  hasSidebar,
1979
2139
  hasTopNav,
1980
2140
  hasFooter,
1981
- shellPattern: inferShellPattern(hasSidebar, hasTopNav, hasFooter)
2141
+ shellPattern: runtimeShellPattern === "main-only" && contractShellPattern ? contractShellPattern : runtimeShellPattern
1982
2142
  };
1983
2143
  }
1984
2144
 
@@ -2062,12 +2222,14 @@ function scanFeatures(projectRoot) {
2062
2222
  }
2063
2223
 
2064
2224
  // src/analyzers/dependencies.ts
2065
- import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
2225
+ import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
2066
2226
  import { join as join17 } from "path";
2067
2227
  var CATEGORIES = {
2068
2228
  ui: [
2069
2229
  "react",
2070
2230
  "react-dom",
2231
+ "react-router",
2232
+ "react-router-dom",
2071
2233
  "vue",
2072
2234
  "svelte",
2073
2235
  "@angular/core",
@@ -2215,7 +2377,7 @@ function scanDependencies(projectRoot) {
2215
2377
  if (!existsSync17(pkgPath)) return result;
2216
2378
  let pkg;
2217
2379
  try {
2218
- pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
2380
+ pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
2219
2381
  } catch {
2220
2382
  return result;
2221
2383
  }
@@ -2235,7 +2397,7 @@ function scanDependencies(projectRoot) {
2235
2397
  }
2236
2398
 
2237
2399
  // src/workflow-model.ts
2238
- import { existsSync as existsSync18, readFileSync as readFileSync14 } from "fs";
2400
+ import { existsSync as existsSync18, readFileSync as readFileSync15 } from "fs";
2239
2401
  import { join as join18 } from "path";
2240
2402
  function inferSuggestedShell(layout) {
2241
2403
  if (layout.hasSidebar) return "sidebar-main";
@@ -2251,6 +2413,7 @@ function createBrownfieldInitSeed(detected, layout, styling) {
2251
2413
  workflow: "brownfield-adoption",
2252
2414
  contractOnly: true,
2253
2415
  registryOptional: true,
2416
+ workflowMode: "brownfield-attach",
2254
2417
  target: detected.framework !== "unknown" ? detected.framework : "react",
2255
2418
  shell: inferSuggestedShell(layout),
2256
2419
  guard: "guided",
@@ -2271,7 +2434,7 @@ function readBrownfieldInitSeed(projectRoot) {
2271
2434
  return null;
2272
2435
  }
2273
2436
  try {
2274
- const parsed = JSON.parse(readFileSync14(seedPath, "utf-8"));
2437
+ const parsed = JSON.parse(readFileSync15(seedPath, "utf-8"));
2275
2438
  if (parsed.workflow !== "brownfield-adoption") {
2276
2439
  return null;
2277
2440
  }
@@ -2917,7 +3080,7 @@ ${GREEN9}${BOLD4}Quality summary:${RESET9}`);
2917
3080
  }
2918
3081
 
2919
3082
  // src/commands/export.ts
2920
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync10, existsSync as existsSync21, mkdirSync as mkdirSync5 } from "fs";
3083
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, existsSync as existsSync21, mkdirSync as mkdirSync5 } from "fs";
2921
3084
  import { join as join21, dirname } from "path";
2922
3085
  var GREEN10 = "\x1B[32m";
2923
3086
  var RED8 = "\x1B[31m";
@@ -3076,7 +3239,7 @@ async function cmdExport(target, projectRoot, options = {}) {
3076
3239
  process.exitCode = 1;
3077
3240
  return;
3078
3241
  }
3079
- const tokensCSS = readFileSync15(tokensPath, "utf-8");
3242
+ const tokensCSS = readFileSync16(tokensPath, "utf-8");
3080
3243
  const tokens = parseTokensCSS(tokensCSS);
3081
3244
  if (tokens.size === 0) {
3082
3245
  console.error(`${RED8}No --d-* tokens found in tokens.css.${RESET10}`);
@@ -3252,7 +3415,7 @@ function seedOfflineRegistry(projectDir, workspaceRoot) {
3252
3415
  }
3253
3416
 
3254
3417
  // src/bootstrap.ts
3255
- import { mkdirSync as mkdirSync8, readFileSync as readFileSync16, writeFileSync as writeFileSync12 } from "fs";
3418
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync17, writeFileSync as writeFileSync12 } from "fs";
3256
3419
  import { basename, join as join24 } from "path";
3257
3420
  import { resolvePackAdapter } from "@decantr/core";
3258
3421
  var reactViteBootstrapAdapter = {
@@ -3432,7 +3595,7 @@ function getBootstrapAdapter(resolution) {
3432
3595
  }
3433
3596
  function detectRoutingMode(projectDir) {
3434
3597
  try {
3435
- const essence = JSON.parse(readFileSync16(join24(projectDir, "decantr.essence.json"), "utf-8"));
3598
+ const essence = JSON.parse(readFileSync17(join24(projectDir, "decantr.essence.json"), "utf-8"));
3436
3599
  const routing = essence.meta?.platform?.routing;
3437
3600
  if (routing === "history" || routing === "pathname") {
3438
3601
  return routing;
@@ -3663,9 +3826,11 @@ function extractPatternName(item) {
3663
3826
  }
3664
3827
  return "custom";
3665
3828
  }
3666
- function generateCuratedPrompt(ctx) {
3829
+ function generateGreenfieldPrompt(ctx) {
3667
3830
  const lines = [];
3668
- lines.push("Build this application using the Decantr design system.");
3831
+ lines.push("Build this greenfield application using the Decantr design system.");
3832
+ lines.push("");
3833
+ lines.push("This workspace is a new Decantr scaffold. Use the contract to create or extend the runtime deliberately, not to reverse-engineer a hidden starter.");
3669
3834
  lines.push("");
3670
3835
  lines.push("Treat the compiled execution-pack files as the primary source of truth.");
3671
3836
  lines.push("Use narrative docs only as secondary explanation when the compiled packs are not enough.");
@@ -3699,6 +3864,50 @@ function generateCuratedPrompt(ctx) {
3699
3864
  lines.push("- If a required context file is missing or inconsistent, stop and report exactly which file is missing before continuing.");
3700
3865
  return lines.join("\n");
3701
3866
  }
3867
+ function generateBrownfieldPrompt(ctx) {
3868
+ const lines = [];
3869
+ lines.push("Attach Decantr to this existing application without rebuilding it from scratch.");
3870
+ lines.push("");
3871
+ lines.push("Preserve the current framework, package manager, router, build tooling, and working runtime structure unless the generated Decantr contract gives you a reviewed reason to change them.");
3872
+ lines.push("");
3873
+ lines.push("Treat .decantr/analysis.json as the factual inventory of the current app.");
3874
+ lines.push("Treat .decantr/init-seed.json as the recommended Decantr attach defaults.");
3875
+ lines.push("Treat the compiled execution-pack files as the Decantr contract you are layering onto the app.");
3876
+ lines.push("Use only files present in this workspace as the source of truth. If the runtime and contract disagree, call out the drift explicitly instead of improvising a rewrite.");
3877
+ lines.push("");
3878
+ lines.push("Read in this order:");
3879
+ lines.push("1. .decantr/analysis.json for the detected framework, routes, styling, layout, and dependencies.");
3880
+ lines.push("2. .decantr/init-seed.json for the intended attach defaults and workflow lane.");
3881
+ lines.push("3. DECANTR.md for guard rules, CSS expectations, and Decantr operating rules.");
3882
+ lines.push("4. .decantr/context/scaffold-pack.md for the compact compiled shell, theme, feature, and route contract.");
3883
+ lines.push("5. .decantr/context/scaffold.md for broader topology, route map, and voice guidance.");
3884
+ lines.push("6. The matching section and page pack files only when you are working on those specific surfaces.");
3885
+ lines.push("");
3886
+ lines.push("Implementation rules:");
3887
+ lines.push("- Preserve existing files and working flows whenever possible. Prefer incremental attachment over whole-app rewrites.");
3888
+ lines.push("- Map existing routes and components onto the declared Decantr sections/pages before creating new files.");
3889
+ lines.push("- If package.json, router files, or style files already exist, extend them deliberately instead of replacing them with a different starter shape.");
3890
+ lines.push("- If Decantr style files are absent, add src/styles/global.css, src/styles/tokens.css, and src/styles/treatments.css in a way that fits the current app structure.");
3891
+ lines.push("- Use the existing Decantr tokens, treatments, and decorators instead of inventing a parallel visual system.");
3892
+ lines.push("- Registry content is optional in this workflow unless the task explicitly asks for blueprint/theme/pattern enrichment.");
3893
+ lines.push("- Do not invent routes, sections, shells, themes, or features that are not present in the compiled packs.");
3894
+ lines.push("- Do not use inline visual style values or component-scoped <style> tags as the primary styling path. Colors, spacing, borders, shadows, gradients, and transitions should come from atoms, treatments, decorators, or CSS variables.");
3895
+ lines.push("- Let shells own spacing, centering, and scroll containers. Preserve app structure, but remove duplicated shell responsibilities when the contract makes them explicit.");
3896
+ lines.push("- If command_palette or hotkeys are declared in the generated context, implement them as real features.");
3897
+ lines.push("- If a required decorator class is referenced in the contract but missing from generated CSS, report the contract gap instead of inventing a parallel visual system.");
3898
+ lines.push("- Do not modify generated context files unless the task is explicitly to regenerate or refresh Decantr context.");
3899
+ lines.push("");
3900
+ lines.push("Execution flow:");
3901
+ lines.push("- Start by inventorying the current runtime and identifying the safest route/component anchors for attachment.");
3902
+ lines.push("- Align the shared shell and route structure incrementally instead of replacing the app shell wholesale.");
3903
+ lines.push("- Then attach or refine section pages using the matching section and page packs.");
3904
+ lines.push("- After implementation, run decantr check and decantr audit and fix contract or drift issues.");
3905
+ lines.push("- If a required context file or runtime anchor is missing, stop and report exactly what is missing before continuing.");
3906
+ return lines.join("\n");
3907
+ }
3908
+ function generateCuratedPrompt(ctx) {
3909
+ return ctx.workflow === "brownfield-attach" ? generateBrownfieldPrompt(ctx) : generateGreenfieldPrompt(ctx);
3910
+ }
3702
3911
  function getAPIClient() {
3703
3912
  return new RegistryAPIClient3({
3704
3913
  baseUrl: process.env.DECANTR_API_URL || void 0,
@@ -3729,13 +3938,13 @@ function readHostedDistSnapshot(distPath) {
3729
3938
  if (!existsSync25(indexPath)) {
3730
3939
  return void 0;
3731
3940
  }
3732
- const indexHtml = readFileSync17(indexPath, "utf-8");
3941
+ const indexHtml = readFileSync18(indexPath, "utf-8");
3733
3942
  const assetPaths = extractHostedAssetPaths(indexHtml);
3734
3943
  const assets = {};
3735
3944
  for (const assetPath of assetPaths) {
3736
3945
  const assetFilePath = join26(resolvedDistPath, assetPath.replace(/^[/\\]+/, ""));
3737
3946
  if (existsSync25(assetFilePath)) {
3738
- assets[assetPath] = readFileSync17(assetFilePath, "utf-8");
3947
+ assets[assetPath] = readFileSync18(assetFilePath, "utf-8");
3739
3948
  }
3740
3949
  }
3741
3950
  return {
@@ -3767,7 +3976,7 @@ function readHostedSourceSnapshot(sourcePath) {
3767
3976
  }
3768
3977
  if (!entry.isFile()) continue;
3769
3978
  if (!isHostedSourceSnapshotFile(relativePath)) continue;
3770
- files[relativePath] = readFileSync17(absolutePath, "utf-8");
3979
+ files[relativePath] = readFileSync18(absolutePath, "utf-8");
3771
3980
  }
3772
3981
  };
3773
3982
  walk(resolvedSourcePath, rootPrefix);
@@ -3888,7 +4097,7 @@ async function printHostedExecutionPackBundle(essencePath, namespace, jsonOutput
3888
4097
  if (!existsSync25(resolvedPath)) {
3889
4098
  throw new Error(`Essence file not found at ${resolvedPath}`);
3890
4099
  }
3891
- const essence = JSON.parse(readFileSync17(resolvedPath, "utf-8"));
4100
+ const essence = JSON.parse(readFileSync18(resolvedPath, "utf-8"));
3892
4101
  const bundle = await client.compileExecutionPacks(
3893
4102
  essence,
3894
4103
  namespace ? { namespace } : void 0
@@ -3938,7 +4147,7 @@ async function printHostedSelectedExecutionPack(packType, id, essencePath, names
3938
4147
  if ((packType === "section" || packType === "page" || packType === "mutation") && !id) {
3939
4148
  throw new Error(`Pack type "${packType}" requires an id.`);
3940
4149
  }
3941
- const essence = JSON.parse(readFileSync17(resolvedPath, "utf-8"));
4150
+ const essence = JSON.parse(readFileSync18(resolvedPath, "utf-8"));
3942
4151
  const selected = await client.selectExecutionPack(
3943
4152
  {
3944
4153
  essence,
@@ -3985,7 +4194,7 @@ async function printHostedExecutionPackManifest(essencePath, namespace, jsonOutp
3985
4194
  if (!existsSync25(resolvedPath)) {
3986
4195
  throw new Error(`Essence file not found at ${resolvedPath}`);
3987
4196
  }
3988
- const essence = JSON.parse(readFileSync17(resolvedPath, "utf-8"));
4197
+ const essence = JSON.parse(readFileSync18(resolvedPath, "utf-8"));
3989
4198
  const manifest = await client.getExecutionPackManifest(
3990
4199
  essence,
3991
4200
  namespace ? { namespace } : void 0
@@ -4031,7 +4240,7 @@ async function hydrateHostedExecutionPacksIfMissing(projectRoot, namespace = "@o
4031
4240
  }
4032
4241
  try {
4033
4242
  const client = getPublicAPIClient();
4034
- const essence = JSON.parse(readFileSync17(essencePath, "utf-8"));
4243
+ const essence = JSON.parse(readFileSync18(essencePath, "utf-8"));
4035
4244
  const bundle = await client.compileExecutionPacks(essence, { namespace });
4036
4245
  mkdirSync10(contextDir, { recursive: true });
4037
4246
  writeExecutionPackBundleArtifacts(
@@ -4056,7 +4265,7 @@ async function hydrateHostedReviewPackIfMissing(projectRoot, namespace = "@offic
4056
4265
  }
4057
4266
  try {
4058
4267
  const client = getPublicAPIClient();
4059
- const essence = JSON.parse(readFileSync17(essencePath, "utf-8"));
4268
+ const essence = JSON.parse(readFileSync18(essencePath, "utf-8"));
4060
4269
  const selected = await client.selectExecutionPack(
4061
4270
  {
4062
4271
  essence,
@@ -4086,9 +4295,9 @@ async function printHostedFileCritique(sourcePath, namespace, jsonOutput = false
4086
4295
  if (!existsSync25(resolvedEssencePath)) {
4087
4296
  throw new Error(`Essence file not found at ${resolvedEssencePath}`);
4088
4297
  }
4089
- const code = readFileSync17(resolvedSourcePath, "utf-8");
4090
- const essence = JSON.parse(readFileSync17(resolvedEssencePath, "utf-8"));
4091
- const treatmentsCss = existsSync25(resolvedTreatmentsPath) ? readFileSync17(resolvedTreatmentsPath, "utf-8") : void 0;
4298
+ const code = readFileSync18(resolvedSourcePath, "utf-8");
4299
+ const essence = JSON.parse(readFileSync18(resolvedEssencePath, "utf-8"));
4300
+ const treatmentsCss = existsSync25(resolvedTreatmentsPath) ? readFileSync18(resolvedTreatmentsPath, "utf-8") : void 0;
4092
4301
  const report = await client.critiqueFile(
4093
4302
  {
4094
4303
  essence,
@@ -4116,7 +4325,7 @@ async function printHostedProjectAudit(namespace, jsonOutput = false, essencePat
4116
4325
  if (!existsSync25(resolvedEssencePath)) {
4117
4326
  throw new Error(`Essence file not found at ${resolvedEssencePath}`);
4118
4327
  }
4119
- const essence = JSON.parse(readFileSync17(resolvedEssencePath, "utf-8"));
4328
+ const essence = JSON.parse(readFileSync18(resolvedEssencePath, "utf-8"));
4120
4329
  const dist = readHostedDistSnapshot(distPath);
4121
4330
  const sources = readHostedSourceSnapshot(sourcesPath);
4122
4331
  const report = await client.auditProject(
@@ -4229,7 +4438,7 @@ async function cmdGet(type, id) {
4229
4438
  ];
4230
4439
  const bundledPath = bundledCandidates.find((p) => existsSync25(p)) || null;
4231
4440
  if (bundledPath) {
4232
- const data = JSON.parse(readFileSync17(bundledPath, "utf-8"));
4441
+ const data = JSON.parse(readFileSync18(bundledPath, "utf-8"));
4233
4442
  console.log(JSON.stringify(data, null, 2));
4234
4443
  return;
4235
4444
  }
@@ -4241,7 +4450,7 @@ async function cmdValidate(path) {
4241
4450
  const essencePath = path || join26(process.cwd(), "decantr.essence.json");
4242
4451
  let raw;
4243
4452
  try {
4244
- raw = readFileSync17(essencePath, "utf-8");
4453
+ raw = readFileSync18(essencePath, "utf-8");
4245
4454
  } catch {
4246
4455
  console.error(error3(`Could not read ${essencePath}`));
4247
4456
  process.exitCode = 1;
@@ -4389,6 +4598,7 @@ async function cmdInit(args) {
4389
4598
  let selectedBlueprint = "default";
4390
4599
  let registrySource = "cache";
4391
4600
  const preferContractOnly = brownfieldAttach && !requestedBlueprint && !requestedArchetype;
4601
+ const workflowMode = workflowSeed || brownfieldAttach && !requestedBlueprint && !requestedArchetype ? "brownfield-attach" : "greenfield-scaffold";
4392
4602
  if (args.yes) {
4393
4603
  selectedBlueprint = args.blueprint || "default";
4394
4604
  } else if (!apiAvailable) {
@@ -4459,6 +4669,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET13}`);
4459
4669
  userExplicit.shape = true;
4460
4670
  userExplicit.personality = true;
4461
4671
  }
4672
+ options.workflowMode = workflowMode;
4462
4673
  let topologyMarkdown = "";
4463
4674
  let archetypeData;
4464
4675
  let composedSections;
@@ -4658,7 +4869,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET13}`);
4658
4869
  console.log(` ${cyan3("decantr upgrade")} Update to latest patterns`);
4659
4870
  console.log(` ${cyan3("decantr check")} Detect drift issues`);
4660
4871
  console.log(` ${cyan3("decantr migrate")} Migrate v2 essence to v3`);
4661
- const essenceContent = readFileSync17(result.essencePath, "utf-8");
4872
+ const essenceContent = readFileSync18(result.essencePath, "utf-8");
4662
4873
  const essence = JSON.parse(essenceContent);
4663
4874
  if (essence.version !== "3.1.0") {
4664
4875
  const validation = validateEssence2(essence);
@@ -4680,6 +4891,7 @@ Validation warnings: ${validation.errors.join(", ")}`));
4680
4891
  promptPages = essence.structure || [{ id: "home", shell: options.shell, layout: ["hero"] }];
4681
4892
  }
4682
4893
  const promptCtx = {
4894
+ workflow: options.workflowMode === "brownfield-attach" ? "brownfield-attach" : "greenfield-scaffold",
4683
4895
  archetype: options.archetype || "custom",
4684
4896
  blueprint: options.blueprint,
4685
4897
  theme: options.theme,
@@ -4714,7 +4926,7 @@ async function cmdStatus() {
4714
4926
  return;
4715
4927
  }
4716
4928
  try {
4717
- const essence = JSON.parse(readFileSync17(essencePath, "utf-8"));
4929
+ const essence = JSON.parse(readFileSync18(essencePath, "utf-8"));
4718
4930
  const validation = validateEssence2(essence);
4719
4931
  const essenceVersion = isV36(essence) ? "v3" : "v2";
4720
4932
  console.log(`${BOLD6}Essence:${RESET13}`);
@@ -4765,7 +4977,7 @@ async function cmdStatus() {
4765
4977
  console.log(`${BOLD6}Sync Status:${RESET13}`);
4766
4978
  if (existsSync25(projectJsonPath)) {
4767
4979
  try {
4768
- const projectJson = JSON.parse(readFileSync17(projectJsonPath, "utf-8"));
4980
+ const projectJson = JSON.parse(readFileSync18(projectJsonPath, "utf-8"));
4769
4981
  const syncStatus = projectJson.sync?.status || "unknown";
4770
4982
  const lastSync = projectJson.sync?.lastSync || "never";
4771
4983
  const source = projectJson.sync?.registrySource || "unknown";
@@ -4979,7 +5191,7 @@ ${BOLD6}Examples:${RESET13}
4979
5191
  return;
4980
5192
  }
4981
5193
  try {
4982
- const theme = JSON.parse(readFileSync17(themePath, "utf-8"));
5194
+ const theme = JSON.parse(readFileSync18(themePath, "utf-8"));
4983
5195
  const result = validateCustomTheme(theme);
4984
5196
  if (result.valid) {
4985
5197
  console.log(success3(`Custom theme "${name}" is valid`));
@@ -5233,7 +5445,7 @@ async function main() {
5233
5445
  break;
5234
5446
  }
5235
5447
  case "upgrade": {
5236
- const { cmdUpgrade } = await import("./upgrade-WE7Y7ZOE.js");
5448
+ const { cmdUpgrade } = await import("./upgrade-UXY2WUJH.js");
5237
5449
  const applyFlag = args.includes("--apply");
5238
5450
  await cmdUpgrade(process.cwd(), { apply: applyFlag });
5239
5451
  break;
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import "./chunk-OHQXL7EJ.js";
2
- import "./chunk-4D5V6GQU.js";
1
+ import "./chunk-ODJQZXHQ.js";
2
+ import "./chunk-74G2RQDO.js";
3
3
  import "./chunk-KUDAVJOR.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  RegistryClient,
3
3
  refreshDerivedFiles
4
- } from "./chunk-4D5V6GQU.js";
4
+ } from "./chunk-74G2RQDO.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.7.7",
3
+ "version": "1.7.8",
4
4
  "description": "Decantr CLI — scaffold, audit, and maintain Decantr projects from the terminal",
5
5
  "author": "Decantr AI",
6
6
  "license": "MIT",
@@ -27,8 +27,8 @@
27
27
  "access": "public"
28
28
  },
29
29
  "dependencies": {
30
- "@decantr/essence-spec": "1.0.1",
31
30
  "@decantr/core": "1.0.0",
31
+ "@decantr/essence-spec": "1.0.1",
32
32
  "@decantr/registry": "1.0.1",
33
33
  "@decantr/verifier": "1.0.1"
34
34
  },
@@ -84,13 +84,9 @@ When a user request would violate guard rules:
84
84
 
85
85
  ### Initial scaffolding
86
86
 
87
- Treat the compiled execution-pack files as the primary source of truth.
88
- Use narrative docs only as secondary explanation when the compiled packs are not enough.
89
- Use only files present in this workspace as the source of truth. If local scaffold files disagree, stop and report the mismatch instead of relying on external Decantr assumptions or prior examples.
87
+ This project is using Decantr in **{{WORKFLOW_MODE}}** mode.
90
88
 
91
- Read `.decantr/context/scaffold-pack.md` first for the compact compiled shell, theme, feature, and route contract.
92
- Then read `.decantr/context/scaffold.md` for the fuller app overview, topology, route map, and voice guidance.
93
- Start implementation from the shell layouts and shared route structure before filling in section pages.
89
+ {{WORKFLOW_GUIDANCE}}
94
90
 
95
91
  ### Working on a section
96
92