@decantr/cli 1.7.24 → 1.7.25
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/README.md
CHANGED
|
@@ -17,19 +17,42 @@ Or run it without installing:
|
|
|
17
17
|
npx @decantr/cli new my-app --blueprint=agent-marketplace
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
Use `decantr new` for a greenfield workspace in a fresh directory.
|
|
20
|
+
Use `decantr new` for a greenfield workspace in a fresh directory. With a blueprint/archetype it uses the runnable adapter and Decantr CSS; without registry content it creates a contract-only workspace unless you explicitly pass `--adoption=decantr-css`.
|
|
21
21
|
Use `decantr analyze` first when you already have an app and want Decantr governance without adopting a blueprint.
|
|
22
22
|
Use `decantr init` to attach Decantr contract/context files to an existing project or to create a contract-only workspace.
|
|
23
23
|
|
|
24
24
|
Current starter adapter availability:
|
|
25
25
|
|
|
26
26
|
- `react-vite` is the runnable bootstrap adapter in this wave
|
|
27
|
-
-
|
|
27
|
+
- `next-app` is the runnable Next.js App Router adapter
|
|
28
|
+
- other contract targets use the `generic-web` contract-only adapter until their runnable adapters land
|
|
29
|
+
|
|
30
|
+
Explicit workflow/adoption flags:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
decantr init --workflow=greenfield --adoption=contract-only
|
|
34
|
+
decantr init --existing --adoption=contract-only
|
|
35
|
+
decantr init --existing --adoption=style-bridge
|
|
36
|
+
decantr init --existing --adoption=decantr-css
|
|
37
|
+
decantr init --project=apps/web --yes
|
|
38
|
+
decantr init --assistant-bridge=preview
|
|
39
|
+
decantr rules apply
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Adoption modes:
|
|
43
|
+
|
|
44
|
+
- `contract-only` writes Decantr essence/context/governance files without Decantr CSS files or `@decantr/css` dependency guidance.
|
|
45
|
+
- `style-bridge` writes bridge tokens/files that map Decantr intent onto an existing style system without requiring `@decantr/css`.
|
|
46
|
+
- `decantr-css` writes the full Decantr CSS files and runtime guidance.
|
|
47
|
+
|
|
48
|
+
Monorepos store both `workspaceRoot` and `appRoot`. In non-interactive workspace-root runs with multiple app candidates, pass `--project=<path>` so Decantr attaches to the intended app.
|
|
49
|
+
|
|
50
|
+
Assistant rule integration is preview-first: `--assistant-bridge=preview` writes `.decantr/context/assistant-bridge.md`, while `--assistant-bridge=apply` or `decantr rules apply` mutates supported rule files with idempotent marked blocks.
|
|
28
51
|
|
|
29
52
|
## What It Does
|
|
30
53
|
|
|
31
54
|
- scaffolds Decantr projects from blueprints, archetypes, or prompts
|
|
32
|
-
- supports
|
|
55
|
+
- supports explicit workflow lanes: greenfield blueprint, greenfield contract-only, brownfield adoption, and hybrid composition
|
|
33
56
|
- generates execution-pack context files for AI coding assistants
|
|
34
57
|
- audits projects against Decantr contracts
|
|
35
58
|
- searches the registry and showcase benchmark corpus
|
|
@@ -40,8 +63,10 @@ Current starter adapter availability:
|
|
|
40
63
|
```bash
|
|
41
64
|
decantr new my-app --blueprint=agent-marketplace
|
|
42
65
|
decantr analyze
|
|
43
|
-
decantr init --existing --yes
|
|
66
|
+
decantr init --existing --yes --adoption=contract-only
|
|
44
67
|
decantr init --existing --blueprint=agent-marketplace
|
|
68
|
+
decantr init --workflow=greenfield --adoption=contract-only
|
|
69
|
+
decantr rules apply
|
|
45
70
|
decantr magic "AI-native analytics workspace"
|
|
46
71
|
decantr audit
|
|
47
72
|
decantr check
|
|
@@ -89,7 +114,14 @@ pnpm --filter @decantr/cli certify:workflows
|
|
|
89
114
|
It covers:
|
|
90
115
|
|
|
91
116
|
- greenfield blueprint bootstrap
|
|
117
|
+
- greenfield contract-only
|
|
92
118
|
- brownfield `analyze -> init --existing`
|
|
119
|
+
- direct brownfield init
|
|
120
|
+
- adoption modes (`contract-only`, `style-bridge`, `decantr-css`)
|
|
121
|
+
- offline contract-only and offline blueprint flows
|
|
122
|
+
- unsupported target contract-only fallback
|
|
123
|
+
- monorepo `--project` handling
|
|
124
|
+
- Next.js App Router adapter
|
|
93
125
|
- hybrid follow-up composition via Decantr mutation commands
|
|
94
126
|
|
|
95
127
|
## Generated Context
|
package/dist/bin.js
CHANGED
|
@@ -40,6 +40,9 @@ var RegistryClient = class {
|
|
|
40
40
|
getApiUrl() {
|
|
41
41
|
return this.apiUrl;
|
|
42
42
|
}
|
|
43
|
+
isOffline() {
|
|
44
|
+
return this.offline;
|
|
45
|
+
}
|
|
43
46
|
/**
|
|
44
47
|
* Load content from .decantr/custom/{contentType}/{id}.json
|
|
45
48
|
* Works for ALL content types, not just themes.
|
|
@@ -587,6 +590,16 @@ ${themeBody}
|
|
|
587
590
|
["font-variant-numeric", "tabular-nums"],
|
|
588
591
|
["transition", "background 0.15s ease, border-color 0.15s ease, color 0.15s ease"]
|
|
589
592
|
]);
|
|
593
|
+
emitRule('.d-step-chip[data-shape="pill"]', [
|
|
594
|
+
["width", "auto"],
|
|
595
|
+
["min-width", "2rem"],
|
|
596
|
+
["height", "auto"],
|
|
597
|
+
["min-height", "2rem"],
|
|
598
|
+
["padding", "0.375rem 0.875rem"],
|
|
599
|
+
["border-radius", "9999px"],
|
|
600
|
+
["gap", "0.375rem"],
|
|
601
|
+
["font-variant-numeric", "normal"]
|
|
602
|
+
]);
|
|
590
603
|
emitRule('.d-step-chip[data-step-state="active"]', [
|
|
591
604
|
["background", "var(--d-primary)"],
|
|
592
605
|
["border-color", "var(--d-primary)"],
|
|
@@ -1319,6 +1332,8 @@ ${themeBody}
|
|
|
1319
1332
|
emitRule('.d-modal-panel[data-size="sm"]', [["max-width", "24rem"]]);
|
|
1320
1333
|
emitRule('.d-modal-panel[data-size="lg"]', [["max-width", "48rem"]]);
|
|
1321
1334
|
emitRule(".d-palette", [
|
|
1335
|
+
["position", "relative"],
|
|
1336
|
+
["z-index", "1"],
|
|
1322
1337
|
["width", "100%"],
|
|
1323
1338
|
["max-width", "40rem"],
|
|
1324
1339
|
["margin-inline", "auto"],
|
|
@@ -1331,34 +1346,73 @@ ${themeBody}
|
|
|
1331
1346
|
["overflow", "hidden"],
|
|
1332
1347
|
["max-height", "60vh"]
|
|
1333
1348
|
]);
|
|
1349
|
+
emitRule(".d-palette-search", [
|
|
1350
|
+
["display", "flex"],
|
|
1351
|
+
["align-items", "center"],
|
|
1352
|
+
["gap", "0.75rem"],
|
|
1353
|
+
["padding", "0.85rem 1rem"],
|
|
1354
|
+
["border-bottom", "1px solid var(--d-border)"],
|
|
1355
|
+
["background", "color-mix(in srgb, var(--d-surface) 82%, transparent)"],
|
|
1356
|
+
["color", "var(--d-text-muted)"],
|
|
1357
|
+
["transition", "background var(--d-motion-fast, 150ms) ease, box-shadow var(--d-motion-fast, 150ms) ease, color var(--d-motion-fast, 150ms) ease"]
|
|
1358
|
+
]);
|
|
1359
|
+
emitRule(".d-palette-search:focus-within", [
|
|
1360
|
+
["background", "var(--d-surface)"],
|
|
1361
|
+
["color", "var(--d-text)"],
|
|
1362
|
+
["box-shadow", "inset 0 -2px 0 var(--d-primary)"]
|
|
1363
|
+
]);
|
|
1334
1364
|
emitRule(".d-palette-input", [
|
|
1335
|
-
["
|
|
1365
|
+
["flex", "1"],
|
|
1366
|
+
["min-width", "0"],
|
|
1367
|
+
["padding", "0.6rem 0"],
|
|
1336
1368
|
["border", "0"],
|
|
1337
|
-
["border-bottom", "1px solid var(--d-border)"],
|
|
1338
1369
|
["background", "transparent"],
|
|
1339
1370
|
["color", "var(--d-text)"],
|
|
1340
|
-
["font-size", "
|
|
1371
|
+
["font-size", "1.05rem"],
|
|
1372
|
+
["line-height", "1.4"],
|
|
1341
1373
|
["outline", "0"],
|
|
1342
1374
|
["width", "100%"]
|
|
1343
1375
|
]);
|
|
1376
|
+
emitRule(".d-palette-input:focus-visible", [
|
|
1377
|
+
["outline", "0"],
|
|
1378
|
+
["box-shadow", "none"]
|
|
1379
|
+
]);
|
|
1380
|
+
emitRule(".d-palette-input::placeholder", [["color", "var(--d-text-muted)"]]);
|
|
1344
1381
|
emitRule(".d-palette-list", [
|
|
1345
1382
|
["flex", "1"],
|
|
1346
1383
|
["overflow-y", "auto"],
|
|
1347
|
-
["padding", "0.5rem"]
|
|
1384
|
+
["padding", "0.5rem"],
|
|
1385
|
+
["display", "grid"],
|
|
1386
|
+
["gap", "0.25rem"]
|
|
1348
1387
|
]);
|
|
1349
1388
|
emitRule(".d-palette-row", [
|
|
1350
1389
|
["display", "flex"],
|
|
1351
1390
|
["align-items", "center"],
|
|
1352
1391
|
["gap", "0.75rem"],
|
|
1353
|
-
["
|
|
1354
|
-
["
|
|
1392
|
+
["width", "100%"],
|
|
1393
|
+
["min-height", "3rem"],
|
|
1394
|
+
["padding", "0.7rem 0.75rem"],
|
|
1395
|
+
["border", "0"],
|
|
1396
|
+
["border-radius", "var(--d-radius)"],
|
|
1397
|
+
["background", "transparent"],
|
|
1355
1398
|
["cursor", "pointer"],
|
|
1356
1399
|
["color", "var(--d-text)"],
|
|
1357
|
-
["font
|
|
1358
|
-
["
|
|
1400
|
+
["font", "inherit"],
|
|
1401
|
+
["font-size", "0.92rem"],
|
|
1402
|
+
["text-align", "left"],
|
|
1403
|
+
["box-shadow", "inset 0 0 0 1px transparent"],
|
|
1404
|
+
["transition", "background 0.12s ease, box-shadow 0.12s ease, color 0.12s ease"]
|
|
1359
1405
|
]);
|
|
1360
1406
|
emitRule('.d-palette-row:hover, .d-palette-row[data-active="true"]', [
|
|
1361
|
-
["background", "color-mix(in srgb, var(--d-primary) 10%, transparent)"]
|
|
1407
|
+
["background", "color-mix(in srgb, var(--d-primary) 10%, transparent)"],
|
|
1408
|
+
["box-shadow", "inset 0 0 0 1px color-mix(in srgb, var(--d-primary) 24%, transparent)"]
|
|
1409
|
+
]);
|
|
1410
|
+
emitRule(".d-palette-row svg", [
|
|
1411
|
+
["flex", "0 0 auto"],
|
|
1412
|
+
["color", "var(--d-text-muted)"]
|
|
1413
|
+
]);
|
|
1414
|
+
emitRule('.d-palette-row:hover svg, .d-palette-row[data-active="true"] svg', [
|
|
1415
|
+
["color", "var(--d-primary)"]
|
|
1362
1416
|
]);
|
|
1363
1417
|
emitRule(".d-palette-section", [
|
|
1364
1418
|
["padding", "0.5rem 0.75rem 0.25rem"],
|
|
@@ -2308,7 +2362,17 @@ function generateTopologySection(data, personality) {
|
|
|
2308
2362
|
lines.push("");
|
|
2309
2363
|
return lines.join("\n");
|
|
2310
2364
|
}
|
|
2311
|
-
|
|
2365
|
+
function readCliVersion() {
|
|
2366
|
+
for (const candidate of [join2(__dirname, "..", "package.json"), join2(__dirname, "..", "..", "package.json")]) {
|
|
2367
|
+
try {
|
|
2368
|
+
const pkg = JSON.parse(readFileSync2(candidate, "utf-8"));
|
|
2369
|
+
if (pkg.version) return pkg.version;
|
|
2370
|
+
} catch {
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
return "0.0.0";
|
|
2374
|
+
}
|
|
2375
|
+
var CLI_VERSION = readCliVersion();
|
|
2312
2376
|
function mapRegistryThemeToThemeData(theme) {
|
|
2313
2377
|
return {
|
|
2314
2378
|
seed: theme.seed,
|
|
@@ -2342,7 +2406,16 @@ function generateTokensCSS(themeData, mode, spatialTokens, options) {
|
|
|
2342
2406
|
--d-surface-raised: #27272a;
|
|
2343
2407
|
--d-border: #3f3f46;
|
|
2344
2408
|
--d-text: #fafafa;
|
|
2345
|
-
--d-text-muted: #a1a1aa
|
|
2409
|
+
--d-text-muted: #a1a1aa;
|
|
2410
|
+
--d-fg: var(--d-text);
|
|
2411
|
+
--d-foreground: var(--d-text);
|
|
2412
|
+
--d-fg-muted: var(--d-text-muted);
|
|
2413
|
+
--d-background: var(--d-bg);
|
|
2414
|
+
--d-muted: var(--d-surface-raised);
|
|
2415
|
+
--d-bg-surface: var(--d-surface);
|
|
2416
|
+
--d-bg-muted: var(--d-surface-raised);
|
|
2417
|
+
--d-border-subtle: var(--d-border);
|
|
2418
|
+
--d-border-muted: var(--d-border);${spatialLines2}
|
|
2346
2419
|
}
|
|
2347
2420
|
}
|
|
2348
2421
|
`;
|
|
@@ -2378,6 +2451,18 @@ function generateTokensCSS(themeData, mode, spatialTokens, options) {
|
|
|
2378
2451
|
"--d-border": palette.border?.[tokenMode] || pickFb("border"),
|
|
2379
2452
|
"--d-text": palette.text?.[tokenMode] || pickFb("text"),
|
|
2380
2453
|
"--d-text-muted": palette["text-muted"]?.[tokenMode] || pickFb("text-muted"),
|
|
2454
|
+
// Compatibility aliases for older registry decorator and pattern copy.
|
|
2455
|
+
// Keep these as var() aliases so theme toggles automatically follow the
|
|
2456
|
+
// active canonical token values without needing duplicate mode blocks.
|
|
2457
|
+
"--d-fg": "var(--d-text)",
|
|
2458
|
+
"--d-foreground": "var(--d-text)",
|
|
2459
|
+
"--d-fg-muted": "var(--d-text-muted)",
|
|
2460
|
+
"--d-background": "var(--d-bg)",
|
|
2461
|
+
"--d-muted": "var(--d-surface-raised)",
|
|
2462
|
+
"--d-bg-surface": "var(--d-surface)",
|
|
2463
|
+
"--d-bg-muted": "var(--d-surface-raised)",
|
|
2464
|
+
"--d-border-subtle": "var(--d-border)",
|
|
2465
|
+
"--d-border-muted": "var(--d-border)",
|
|
2381
2466
|
"--d-primary-hover": palette["primary-hover"]?.[tokenMode] || seed.primary || "#6366f1",
|
|
2382
2467
|
// Spacing scale
|
|
2383
2468
|
"--d-gap-1": "0.25rem",
|
|
@@ -2412,6 +2497,8 @@ function generateTokensCSS(themeData, mode, spatialTokens, options) {
|
|
|
2412
2497
|
"--d-error": themeData.tokens?.base?.danger || "#ef4444",
|
|
2413
2498
|
"--d-warning": themeData.tokens?.base?.warning || "#f59e0b",
|
|
2414
2499
|
"--d-info": "#3b82f6",
|
|
2500
|
+
"--d-danger": "var(--d-error)",
|
|
2501
|
+
"--d-destructive": "var(--d-error)",
|
|
2415
2502
|
// Motion scale (v2.1 Tier B1). Canonical durations + easings.
|
|
2416
2503
|
// d-enter-fade, d-pulse, d-glow-hover, etc. all read these.
|
|
2417
2504
|
// Themes can override via theme.motion.* and this picks them up
|
|
@@ -3070,6 +3157,7 @@ Do NOT branch component code on the current mode via JS to re-style elements \u2
|
|
|
3070
3157
|
| **Modal backdrop** | \`d-modal-backdrop\` | Scrim with backdrop-blur. Place as a sibling inside \`d-modal\` with \`onClick\` to close. |
|
|
3071
3158
|
| **Modal panel** | \`d-modal-panel\` | The actual dialog content. \`data-size="sm\\|lg"\` adjusts max-width (default 32rem). |
|
|
3072
3159
|
| **Command palette** | \`d-palette\` | Specialized modal-panel variant for command palettes \u2014 40rem wide, 60vh max-height. |
|
|
3160
|
+
| **Palette search row** | \`d-palette-search\` | Icon + search input row at the top of a palette. Use this wrapper so focus styling belongs to the palette, not the raw input. |
|
|
3073
3161
|
| **Palette input** | \`d-palette-input\` | Search input at top of palette. |
|
|
3074
3162
|
| **Palette list** | \`d-palette-list\` | Scrollable command list. |
|
|
3075
3163
|
| **Palette row** | \`d-palette-row\` | Individual command row. \`data-active="true"\` for keyboard-highlighted row. |
|
|
@@ -3083,7 +3171,10 @@ Composition pattern for a command palette (REQUIRED \u2014 palette MUST be wrapp
|
|
|
3083
3171
|
<div className="d-modal" data-align="top">
|
|
3084
3172
|
<div className="d-modal-backdrop" onClick={close} />
|
|
3085
3173
|
<div className="d-palette">
|
|
3086
|
-
<
|
|
3174
|
+
<div className="d-palette-search">
|
|
3175
|
+
<Search />
|
|
3176
|
+
<input className="d-palette-input" placeholder="Type a command..." />
|
|
3177
|
+
</div>
|
|
3087
3178
|
<ul className="d-palette-list">
|
|
3088
3179
|
<li className="d-palette-section">Navigation</li>
|
|
3089
3180
|
<li className="d-palette-row" data-active={i === selectedIndex}>
|
|
@@ -3565,19 +3656,48 @@ Dark themes emit stronger alpha values automatically.
|
|
|
3565
3656
|
| \`shimmer-skeleton\` | \`d-shimmer\` on skeleton placeholders during loading |
|
|
3566
3657
|
| \`zoom-pinch\` | Touch handlers (\`touchstart\`/\`touchmove\`) tracking pinch distance, OR \`gestureend\` on Safari; same scale transform as \`zoom-scroll\` |
|
|
3567
3658
|
| \`ripple-click\` | \`d-ripple\` on the interactive surface |`;
|
|
3659
|
+
var CONTRACT_ONLY_CSS_APPROACH = `## Styling Adoption
|
|
3660
|
+
|
|
3661
|
+
This project uses Decantr as a **contract and governance layer only**.
|
|
3662
|
+
|
|
3663
|
+
Do not install \`@decantr/css\`, rewrite the styling system, or add generated Decantr CSS files unless the task explicitly changes the adoption mode. Preserve the existing framework styling conventions and map them into the Decantr context before changing implementation files.
|
|
3664
|
+
|
|
3665
|
+
Use \`.decantr/context/scaffold-pack.md\` and the matching section/page packs to understand visual intent, shell structure, and route contracts. Implement those contracts through the project's current CSS, component library, tokens, or design-system primitives.`;
|
|
3666
|
+
var STYLE_BRIDGE_CSS_APPROACH = `## Styling Adoption
|
|
3667
|
+
|
|
3668
|
+
This project uses Decantr in **style-bridge** mode.
|
|
3669
|
+
|
|
3670
|
+
Decantr may generate lightweight bridge files such as \`src/styles/tokens.css\` and \`src/styles/decantr-bridge.css\`, but \`@decantr/css\` is not required. Treat these files as a mapping layer between Decantr context and the app's existing styling system.
|
|
3671
|
+
|
|
3672
|
+
Preserve the current CSS framework/component library. Use Decantr tokens and bridge classes only where they clarify design intent without replacing the app's established styling conventions.`;
|
|
3673
|
+
function getCssApproachContent(adoptionMode) {
|
|
3674
|
+
if (adoptionMode === "contract-only") return CONTRACT_ONLY_CSS_APPROACH;
|
|
3675
|
+
if (adoptionMode === "style-bridge") return STYLE_BRIDGE_CSS_APPROACH;
|
|
3676
|
+
return CSS_APPROACH_CONTENT;
|
|
3677
|
+
}
|
|
3568
3678
|
function generateDecantrMdV31(params) {
|
|
3569
3679
|
const template = loadTemplate("DECANTR.md.template");
|
|
3570
3680
|
const body = renderTemplate(template, {
|
|
3571
3681
|
GUARD_MODE: params.guardMode,
|
|
3572
3682
|
CSS_APPROACH: params.cssApproach,
|
|
3573
|
-
WORKFLOW_MODE: params.workflowMode === "brownfield-attach" ? "brownfield attach" : "greenfield scaffold",
|
|
3574
|
-
WORKFLOW_GUIDANCE: params.workflowMode === "brownfield-attach" ? `This project is using Decantr in **brownfield attach** mode.
|
|
3683
|
+
WORKFLOW_MODE: params.workflowMode === "brownfield-attach" ? "brownfield attach" : params.workflowMode === "greenfield-contract-only" ? "greenfield contract-only" : params.workflowMode === "hybrid-compose" ? "hybrid composition" : "greenfield scaffold",
|
|
3684
|
+
WORKFLOW_GUIDANCE: params.workflowMode === "brownfield-attach" ? params.analysisArtifacts ? `This project is using Decantr in **brownfield attach** mode with **${params.adoptionMode || "contract-only"}** adoption.
|
|
3575
3685
|
|
|
3576
3686
|
Read \`.decantr/analysis.json\` first for the detected framework, routes, styling, layout, and dependency facts.
|
|
3577
3687
|
Then read \`.decantr/init-seed.json\` for the recommended attach defaults.
|
|
3578
3688
|
Then read \`.decantr/context/scaffold-pack.md\` and \`.decantr/context/scaffold.md\` to understand the Decantr contract you are layering onto the existing app.
|
|
3579
3689
|
|
|
3580
|
-
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 **
|
|
3690
|
+
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 **brownfield attach** mode with **${params.adoptionMode || "contract-only"}** adoption.
|
|
3691
|
+
|
|
3692
|
+
No \`.decantr/analysis.json\` or \`.decantr/init-seed.json\` was present when this context was generated. Inventory the current framework, routes, styling, layout, package manager, and rule files before changing runtime code. Then read \`.decantr/context/scaffold-pack.md\` and \`.decantr/context/scaffold.md\` to understand the Decantr contract you are layering onto the existing app.
|
|
3693
|
+
|
|
3694
|
+
Preserve the current framework, package manager, router, and working runtime structure unless the contract gives you a reviewed reason to change them. Registry content is optional in this workflow unless the task explicitly asks for it.` : params.workflowMode === "greenfield-contract-only" ? `This project is using Decantr in **greenfield contract-only** mode with **${params.adoptionMode || "contract-only"}** adoption.
|
|
3695
|
+
|
|
3696
|
+
Treat the compiled execution-pack files as the primary source of truth for the app contract, but do not assume Decantr owns the runtime or styling system. Use narrative docs only as secondary explanation when the compiled packs are not enough.
|
|
3697
|
+
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.
|
|
3698
|
+
|
|
3699
|
+
Read \`.decantr/context/scaffold-pack.md\` first for the compact compiled shell, theme, feature, and route contract.
|
|
3700
|
+
Then read \`.decantr/context/scaffold.md\` for the fuller app overview, topology, route map, and voice guidance.` : `This project is using Decantr in **greenfield scaffold** mode with **${params.adoptionMode || "decantr-css"}** adoption.
|
|
3581
3701
|
|
|
3582
3702
|
Treat the compiled execution-pack files as the primary source of truth.
|
|
3583
3703
|
Use narrative docs only as secondary explanation when the compiled packs are not enough.
|
|
@@ -3594,8 +3714,9 @@ Start implementation from the shell layouts and shared route structure before fi
|
|
|
3594
3714
|
const themeDesc = `${params.themeName || "default"} (${params.themeMode || "dark"} mode${params.themeShape ? `, ${params.themeShape} shape` : ""})`;
|
|
3595
3715
|
briefLines.push(`- **Theme:** ${themeDesc}`);
|
|
3596
3716
|
briefLines.push(
|
|
3597
|
-
`- **Workflow:** ${params.workflowMode
|
|
3717
|
+
`- **Workflow:** ${params.workflowMode || "greenfield-scaffold"}`
|
|
3598
3718
|
);
|
|
3719
|
+
briefLines.push(`- **Adoption mode:** ${params.adoptionMode || "decantr-css"}`);
|
|
3599
3720
|
if (params.personality && params.personality.length > 0) {
|
|
3600
3721
|
briefLines.push(`- **Personality:** ${params.personality.join(". ")}`);
|
|
3601
3722
|
}
|
|
@@ -3683,7 +3804,9 @@ function generateProjectJson(detected, options, registrySource) {
|
|
|
3683
3804
|
packageManager: detected.packageManager,
|
|
3684
3805
|
hasTypeScript: detected.hasTypeScript,
|
|
3685
3806
|
hasTailwind: detected.hasTailwind,
|
|
3686
|
-
existingRuleFiles: detected.existingRuleFiles
|
|
3807
|
+
existingRuleFiles: detected.existingRuleFiles,
|
|
3808
|
+
workspaceRoot: options.workspaceRoot || detected.projectRoot,
|
|
3809
|
+
appRoot: options.appRoot || detected.projectRoot
|
|
3687
3810
|
},
|
|
3688
3811
|
overrides: {
|
|
3689
3812
|
framework: options.target !== detected.framework ? options.target : null
|
|
@@ -3703,7 +3826,13 @@ function generateProjectJson(detected, options, registrySource) {
|
|
|
3703
3826
|
via: "cli",
|
|
3704
3827
|
version: CLI_VERSION,
|
|
3705
3828
|
flags: buildFlagsString(options),
|
|
3706
|
-
workflowMode: options.workflowMode || "greenfield-scaffold"
|
|
3829
|
+
workflowMode: options.workflowMode || "greenfield-scaffold",
|
|
3830
|
+
adoptionMode: options.adoptionMode || "decantr-css",
|
|
3831
|
+
contentSource: options.contentSource || "none",
|
|
3832
|
+
assistantBridge: options.assistantBridge || "none",
|
|
3833
|
+
projectScope: options.projectScope || "single-app",
|
|
3834
|
+
adapterId: options.adapterId || null,
|
|
3835
|
+
analysisArtifacts: Boolean(options.analysisArtifacts)
|
|
3707
3836
|
}
|
|
3708
3837
|
};
|
|
3709
3838
|
if (options.blueprint) {
|
|
@@ -3717,6 +3846,12 @@ function buildFlagsString(options) {
|
|
|
3717
3846
|
if (options.theme) flags.push(`--theme=${options.theme}`);
|
|
3718
3847
|
if (options.mode) flags.push(`--mode=${options.mode}`);
|
|
3719
3848
|
if (options.guard) flags.push(`--guard=${options.guard}`);
|
|
3849
|
+
if (options.workflowMode) flags.push(`--workflow=${options.workflowMode}`);
|
|
3850
|
+
if (options.adoptionMode) flags.push(`--adoption=${options.adoptionMode}`);
|
|
3851
|
+
if (options.assistantBridge) flags.push(`--assistant-bridge=${options.assistantBridge}`);
|
|
3852
|
+
if (options.appRoot && options.workspaceRoot && options.appRoot !== options.workspaceRoot) {
|
|
3853
|
+
flags.push(`--project=${options.appRoot}`);
|
|
3854
|
+
}
|
|
3720
3855
|
return flags.join(" ");
|
|
3721
3856
|
}
|
|
3722
3857
|
function generateTaskContextV3(templateName, essence) {
|
|
@@ -3775,7 +3910,8 @@ function generateScaffoldTaskContext(essence, scaffoldPack, manifest) {
|
|
|
3775
3910
|
const features = scaffoldPack.data.features.length > 0 ? scaffoldPack.data.features.join(", ") : "none";
|
|
3776
3911
|
const routePlan = scaffoldPack.data.routes.length > 0 ? scaffoldPack.data.routes.map((route) => {
|
|
3777
3912
|
const patternSummary = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
3778
|
-
|
|
3913
|
+
const pageLabel = route.sectionId ? `${route.sectionId}/${route.pageId}` : route.pageId;
|
|
3914
|
+
return `- \`${route.path}\` -> \`${pageLabel}\` [${patternSummary}]`;
|
|
3779
3915
|
}).join("\n") : "- No routes declared";
|
|
3780
3916
|
const successChecks = scaffoldPack.successChecks.map((check) => `- [${check.severity}] ${check.label}`).join("\n");
|
|
3781
3917
|
const tokenStrategy = scaffoldPack.tokenBudget.strategy.map((item) => `- ${item}`).join("\n");
|
|
@@ -3831,7 +3967,8 @@ function generateAddPageTaskContext(essence, scaffoldPack, manifest) {
|
|
|
3831
3967
|
}
|
|
3832
3968
|
const routePlan = scaffoldPack.data.routes.length > 0 ? scaffoldPack.data.routes.map((route) => {
|
|
3833
3969
|
const patternSummary = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
3834
|
-
|
|
3970
|
+
const pageLabel = route.sectionId ? `${route.sectionId}/${route.pageId}` : route.pageId;
|
|
3971
|
+
return `- \`${route.path}\` -> \`${pageLabel}\` [${patternSummary}]`;
|
|
3835
3972
|
}).join("\n") : "- No routes declared";
|
|
3836
3973
|
const sectionRefs = manifest?.sections.map(
|
|
3837
3974
|
(section) => `Section \`${section.id}\` -> \`.decantr/context/${section.markdown}\``
|
|
@@ -3887,7 +4024,8 @@ function generateModifyTaskContext(essence, scaffoldPack, manifest) {
|
|
|
3887
4024
|
}
|
|
3888
4025
|
const routePlan = scaffoldPack.data.routes.length > 0 ? scaffoldPack.data.routes.map((route) => {
|
|
3889
4026
|
const patternSummary = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
3890
|
-
|
|
4027
|
+
const pageLabel = route.sectionId ? `${route.sectionId}/${route.pageId}` : route.pageId;
|
|
4028
|
+
return `- \`${route.path}\` -> \`${pageLabel}\` [${patternSummary}]`;
|
|
3891
4029
|
}).join("\n") : "- No routes declared";
|
|
3892
4030
|
const pageRefs = manifest?.pages.map((page) => `Page \`${page.id}\` -> \`.decantr/context/${page.markdown}\``) ?? [];
|
|
3893
4031
|
const successChecks = scaffoldPack.successChecks.map((check) => `- [${check.severity}] ${check.label}`).join("\n");
|
|
@@ -4033,7 +4171,10 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
|
|
|
4033
4171
|
}
|
|
4034
4172
|
const refreshResult = await refreshDerivedFiles(projectRoot, essenceV3, registry, themeData, {
|
|
4035
4173
|
isInitialScaffold: true,
|
|
4036
|
-
patternSpecs
|
|
4174
|
+
patternSpecs,
|
|
4175
|
+
workflowMode: options.workflowMode,
|
|
4176
|
+
adoptionMode: options.adoptionMode,
|
|
4177
|
+
analysisArtifacts: options.analysisArtifacts
|
|
4037
4178
|
});
|
|
4038
4179
|
contextFiles.push(...refreshResult.contextFiles);
|
|
4039
4180
|
const gitignoreUpdated = updateGitignore(projectRoot);
|
|
@@ -4046,7 +4187,7 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
|
|
|
4046
4187
|
gitignoreUpdated
|
|
4047
4188
|
};
|
|
4048
4189
|
}
|
|
4049
|
-
function scaffoldMinimal(projectRoot) {
|
|
4190
|
+
function scaffoldMinimal(projectRoot, options = {}) {
|
|
4050
4191
|
const decantrDir = join2(projectRoot, ".decantr");
|
|
4051
4192
|
const customDir = join2(decantrDir, "custom");
|
|
4052
4193
|
const contentTypes = API_CONTENT_TYPES2;
|
|
@@ -4143,7 +4284,12 @@ function scaffoldMinimal(projectRoot) {
|
|
|
4143
4284
|
via: "cli",
|
|
4144
4285
|
version: CLI_VERSION,
|
|
4145
4286
|
flags: "--offline --minimal",
|
|
4146
|
-
workflowMode: "greenfield-
|
|
4287
|
+
workflowMode: options.workflowMode || "greenfield-contract-only",
|
|
4288
|
+
adoptionMode: options.adoptionMode || "contract-only",
|
|
4289
|
+
contentSource: options.contentSource || "none",
|
|
4290
|
+
assistantBridge: options.assistantBridge || "none",
|
|
4291
|
+
projectScope: "single-app",
|
|
4292
|
+
analysisArtifacts: false
|
|
4147
4293
|
}
|
|
4148
4294
|
};
|
|
4149
4295
|
const projectJsonPath = join2(decantrDir, "project.json");
|
|
@@ -4257,9 +4403,11 @@ function writeExecutionPackBundleArtifacts(contextDir, bundle) {
|
|
|
4257
4403
|
);
|
|
4258
4404
|
outputPaths.push(sectionPackPath);
|
|
4259
4405
|
}
|
|
4260
|
-
for (const pagePack of bundle.pages) {
|
|
4406
|
+
for (const [index, pagePack] of bundle.pages.entries()) {
|
|
4407
|
+
const manifestPage = bundle.manifest.pages[index];
|
|
4408
|
+
const pageBaseName = manifestPage?.markdown?.endsWith(".md") ? manifestPage.markdown.slice(0, -".md".length) : `page-${pagePack.data.pageId}-pack`;
|
|
4261
4409
|
const pagePackPath = writeExecutionPackArtifacts(
|
|
4262
|
-
join2(contextDir,
|
|
4410
|
+
join2(contextDir, pageBaseName),
|
|
4263
4411
|
pagePack
|
|
4264
4412
|
);
|
|
4265
4413
|
outputPaths.push(pagePackPath);
|
|
@@ -4369,6 +4517,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4369
4517
|
let storedBlueprintId;
|
|
4370
4518
|
let storedVoice;
|
|
4371
4519
|
let storedWorkflowMode;
|
|
4520
|
+
let storedAdoptionMode;
|
|
4521
|
+
let storedAnalysisArtifacts = false;
|
|
4372
4522
|
const projectJsonFilePath = join2(decantrDir, "project.json");
|
|
4373
4523
|
let projectJsonData = {};
|
|
4374
4524
|
if (existsSync2(projectJsonFilePath)) {
|
|
@@ -4378,9 +4528,16 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4378
4528
|
if (projectJsonData.voice) storedVoice = projectJsonData.voice;
|
|
4379
4529
|
if (projectJsonData.initialized?.workflowMode)
|
|
4380
4530
|
storedWorkflowMode = projectJsonData.initialized.workflowMode;
|
|
4531
|
+
if (projectJsonData.initialized?.adoptionMode)
|
|
4532
|
+
storedAdoptionMode = projectJsonData.initialized.adoptionMode;
|
|
4533
|
+
if (projectJsonData.initialized?.analysisArtifacts)
|
|
4534
|
+
storedAnalysisArtifacts = projectJsonData.initialized.analysisArtifacts;
|
|
4381
4535
|
} catch {
|
|
4382
4536
|
}
|
|
4383
4537
|
}
|
|
4538
|
+
const effectiveWorkflowMode = options?.workflowMode || storedWorkflowMode || "greenfield-scaffold";
|
|
4539
|
+
const effectiveAdoptionMode = options?.adoptionMode || storedAdoptionMode || "decantr-css";
|
|
4540
|
+
const effectiveAnalysisArtifacts = options?.analysisArtifacts ?? storedAnalysisArtifacts ?? false;
|
|
4384
4541
|
if (!storedVoice && storedBlueprintId) {
|
|
4385
4542
|
try {
|
|
4386
4543
|
const bpResult = await registry.fetchBlueprint(storedBlueprintId);
|
|
@@ -4404,7 +4561,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4404
4561
|
};
|
|
4405
4562
|
const personality = essence.dna.personality || [];
|
|
4406
4563
|
let themeData = prefetchedThemeData;
|
|
4407
|
-
|
|
4564
|
+
const shouldResolveThemeData = effectiveAdoptionMode !== "contract-only" || Boolean(themeData);
|
|
4565
|
+
if (!themeData && shouldResolveThemeData)
|
|
4408
4566
|
try {
|
|
4409
4567
|
const themeResult = await registry.fetchTheme(themeName);
|
|
4410
4568
|
if (themeResult?.data) {
|
|
@@ -4412,7 +4570,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4412
4570
|
}
|
|
4413
4571
|
} catch {
|
|
4414
4572
|
}
|
|
4415
|
-
|
|
4573
|
+
const registryIsOffline = typeof registry.isOffline === "function" ? registry.isOffline() : false;
|
|
4574
|
+
if (shouldResolveThemeData && !themeData?.seed?.primary && !registryIsOffline) {
|
|
4416
4575
|
try {
|
|
4417
4576
|
const apiUrl = registry.getApiUrl();
|
|
4418
4577
|
const resp = await fetch(`${apiUrl}/themes/@official/${themeName}`);
|
|
@@ -4442,7 +4601,10 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4442
4601
|
}
|
|
4443
4602
|
}
|
|
4444
4603
|
const stylesDir = join2(projectRoot, "src", "styles");
|
|
4445
|
-
|
|
4604
|
+
const shouldWriteCss = effectiveAdoptionMode !== "contract-only";
|
|
4605
|
+
if (shouldWriteCss) {
|
|
4606
|
+
mkdirSync2(stylesDir, { recursive: true });
|
|
4607
|
+
}
|
|
4446
4608
|
const densityLevel = essence.dna?.spacing?.density || "comfortable";
|
|
4447
4609
|
const spatialTokens = computeSpatialTokens(
|
|
4448
4610
|
densityLevel,
|
|
@@ -4453,8 +4615,9 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4453
4615
|
} : void 0
|
|
4454
4616
|
);
|
|
4455
4617
|
const tokensPath = join2(stylesDir, "tokens.css");
|
|
4618
|
+
const bridgePath = join2(stylesDir, "decantr-bridge.css");
|
|
4456
4619
|
const hasRealThemeData = themeData?.seed?.primary || themeData?.palette?.background;
|
|
4457
|
-
if (hasRealThemeData || !existsSync2(tokensPath)) {
|
|
4620
|
+
if (shouldWriteCss && (hasRealThemeData || !existsSync2(tokensPath))) {
|
|
4458
4621
|
if (themeData?.palette && mode && mode !== "auto") {
|
|
4459
4622
|
const paletteEntries = Object.values(themeData.palette);
|
|
4460
4623
|
const modeDefined = paletteEntries.some(
|
|
@@ -4489,27 +4652,36 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4489
4652
|
}
|
|
4490
4653
|
const features = essence.blueprint?.features ?? [];
|
|
4491
4654
|
const hasThemeToggle = features.includes("theme-toggle") || features.includes("theme_toggle");
|
|
4655
|
+
writeFileSync2(tokensPath, generateTokensCSS(themeData, mode, spatialTokens, { hasThemeToggle }));
|
|
4656
|
+
}
|
|
4657
|
+
const treatmentsPath = join2(stylesDir, "treatments.css");
|
|
4658
|
+
if (effectiveAdoptionMode === "decantr-css") {
|
|
4659
|
+
let treatmentCSS = generateTreatmentCSS(
|
|
4660
|
+
spatialTokens,
|
|
4661
|
+
themeData?.treatments,
|
|
4662
|
+
themeData?.decorators,
|
|
4663
|
+
themeName,
|
|
4664
|
+
themeData?.decorator_definitions
|
|
4665
|
+
);
|
|
4666
|
+
const personalityCSS = generatePersonalityCSS(personality || [], themeData || {});
|
|
4667
|
+
treatmentCSS += personalityCSS;
|
|
4668
|
+
writeFileSync2(treatmentsPath, treatmentCSS);
|
|
4669
|
+
} else if (effectiveAdoptionMode === "style-bridge") {
|
|
4492
4670
|
writeFileSync2(
|
|
4493
|
-
|
|
4494
|
-
|
|
4671
|
+
bridgePath,
|
|
4672
|
+
`/* Decantr style bridge: map these CSS variables into the existing design system. */
|
|
4673
|
+
:root {
|
|
4674
|
+
--decantr-bridge-theme: ${themeName};
|
|
4675
|
+
--decantr-bridge-density: ${densityLevel};
|
|
4676
|
+
}
|
|
4677
|
+
`
|
|
4495
4678
|
);
|
|
4496
4679
|
}
|
|
4497
|
-
const treatmentsPath = join2(stylesDir, "treatments.css");
|
|
4498
|
-
let treatmentCSS = generateTreatmentCSS(
|
|
4499
|
-
spatialTokens,
|
|
4500
|
-
themeData?.treatments,
|
|
4501
|
-
themeData?.decorators,
|
|
4502
|
-
themeName,
|
|
4503
|
-
themeData?.decorator_definitions
|
|
4504
|
-
);
|
|
4505
|
-
const personalityCSS = generatePersonalityCSS(personality || [], themeData || {});
|
|
4506
|
-
treatmentCSS += personalityCSS;
|
|
4507
|
-
writeFileSync2(treatmentsPath, treatmentCSS);
|
|
4508
4680
|
const globalPath = join2(stylesDir, "global.css");
|
|
4509
|
-
if (!existsSync2(globalPath)) {
|
|
4681
|
+
if (shouldWriteCss && !existsSync2(globalPath)) {
|
|
4510
4682
|
writeFileSync2(globalPath, generateGlobalCSS(personality, essence));
|
|
4511
4683
|
}
|
|
4512
|
-
const cssFiles = [tokensPath, treatmentsPath, globalPath];
|
|
4684
|
+
const cssFiles = effectiveAdoptionMode === "contract-only" ? [] : effectiveAdoptionMode === "style-bridge" ? [tokensPath, bridgePath, globalPath] : [tokensPath, treatmentsPath, globalPath];
|
|
4513
4685
|
const earlyDecoratorList = [];
|
|
4514
4686
|
if (themeData?.decorators) {
|
|
4515
4687
|
for (const [name, desc] of Object.entries(themeData.decorators)) {
|
|
@@ -4538,8 +4710,10 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4538
4710
|
decantrMdPath,
|
|
4539
4711
|
generateDecantrMdV31({
|
|
4540
4712
|
guardMode,
|
|
4541
|
-
cssApproach:
|
|
4542
|
-
workflowMode:
|
|
4713
|
+
cssApproach: getCssApproachContent(effectiveAdoptionMode),
|
|
4714
|
+
workflowMode: effectiveWorkflowMode,
|
|
4715
|
+
adoptionMode: effectiveAdoptionMode,
|
|
4716
|
+
analysisArtifacts: effectiveAnalysisArtifacts,
|
|
4543
4717
|
blueprintId: storedBlueprintId || getLegacyBlueprintId(essence.meta) || void 0,
|
|
4544
4718
|
themeName,
|
|
4545
4719
|
themeMode: mode,
|