@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 +2 -2
- package/dist/{chunk-4D5V6GQU.js → chunk-74G2RQDO.js} +25 -3
- package/dist/{chunk-OHQXL7EJ.js → chunk-ODJQZXHQ.js} +263 -51
- package/dist/index.js +2 -2
- package/dist/{upgrade-WE7Y7ZOE.js → upgrade-UXY2WUJH.js} +1 -1
- package/package.json +2 -2
- package/src/templates/DECANTR.md.template +2 -6
package/dist/bin.js
CHANGED
|
@@ -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-
|
|
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
|
|
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
|
|
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,
|
|
1797
|
-
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2001
|
+
for (const cssContent of cssContents) {
|
|
1870
2002
|
const extracted = extractCSSVariables(cssContent);
|
|
1871
|
-
colors = extracted.colors;
|
|
1872
|
-
cssVariables
|
|
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
|
|
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 =
|
|
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 =
|
|
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:
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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] =
|
|
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] =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
4090
|
-
const essence = JSON.parse(
|
|
4091
|
-
const treatmentsCss = existsSync25(resolvedTreatmentsPath) ?
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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-
|
|
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-
|
|
2
|
-
import "./chunk-
|
|
1
|
+
import "./chunk-ODJQZXHQ.js";
|
|
2
|
+
import "./chunk-74G2RQDO.js";
|
|
3
3
|
import "./chunk-KUDAVJOR.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decantr/cli",
|
|
3
|
-
"version": "1.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
|
-
|
|
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
|
-
|
|
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
|
|