@decantr/cli 1.7.23 → 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
|
|
@@ -2889,12 +2976,15 @@ import './styles/global.css'; // Resets
|
|
|
2889
2976
|
| \u274C Inline (DO NOT) | \u2705 Atom (DO) |
|
|
2890
2977
|
|--------------------|------------|
|
|
2891
2978
|
| \`style={{ display: 'flex', gap: '1rem' }}\` | \`className={css('_flex _gap4')}\` |
|
|
2892
|
-
| \`style={{ flexDirection: 'column', alignItems: 'center' }}\` | \`className={css('_col
|
|
2979
|
+
| \`style={{ flexDirection: 'column', alignItems: 'center' }}\` | \`className={css('_col _aic')}\` |
|
|
2893
2980
|
| \`style={{ padding: '1rem 1.5rem' }}\` | \`className={css('_py4 _px6')}\` |
|
|
2894
|
-
| \`style={{ width: '100%', maxWidth: '40rem' }}\` | \`className={css('
|
|
2981
|
+
| \`style={{ width: '100%', maxWidth: '40rem' }}\` | \`className={css('_wfull _maxw[40rem]')}\` |
|
|
2895
2982
|
| \`style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)' }}\` | \`className={css('_grid _gc3')}\` |
|
|
2896
|
-
| \`style={{ position: 'sticky', top: 0 }}\` | \`className={css('_sticky
|
|
2983
|
+
| \`style={{ position: 'sticky', top: 0 }}\` | \`className={css('_sticky _top0')}\` |
|
|
2897
2984
|
| \`style={{ marginInline: 'auto' }}\` | \`className={css('_mxauto')}\` |
|
|
2985
|
+
| \`style={{ fontSize: '1.5rem' }}\` | \`className={css('_text2xl')}\` |
|
|
2986
|
+
|
|
2987
|
+
> **Naming convention:** atoms use compact prefix-spelling (\`_aic\`, \`_jcsb\`, \`_wfull\`, \`_top0\`) \u2014 not Tailwind-style hyphenation. The runtime accepts both (\`_aic\` and \`_items-center\` both resolve to \`align-items:center\`) but compact prefix is canonical and shorter.
|
|
2898
2988
|
|
|
2899
2989
|
**Inline \`style={{ ... }}\` is ONLY acceptable for these cases:**
|
|
2900
2990
|
1. **CSS custom property writes** \u2014 \`style={{ '--d-stagger-index': i }}\` (the contract REQUIRES inline writes for dynamic CSS-var values that loop variables / animation indices feed into).
|
|
@@ -3067,6 +3157,7 @@ Do NOT branch component code on the current mode via JS to re-style elements \u2
|
|
|
3067
3157
|
| **Modal backdrop** | \`d-modal-backdrop\` | Scrim with backdrop-blur. Place as a sibling inside \`d-modal\` with \`onClick\` to close. |
|
|
3068
3158
|
| **Modal panel** | \`d-modal-panel\` | The actual dialog content. \`data-size="sm\\|lg"\` adjusts max-width (default 32rem). |
|
|
3069
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. |
|
|
3070
3161
|
| **Palette input** | \`d-palette-input\` | Search input at top of palette. |
|
|
3071
3162
|
| **Palette list** | \`d-palette-list\` | Scrollable command list. |
|
|
3072
3163
|
| **Palette row** | \`d-palette-row\` | Individual command row. \`data-active="true"\` for keyboard-highlighted row. |
|
|
@@ -3080,7 +3171,10 @@ Composition pattern for a command palette (REQUIRED \u2014 palette MUST be wrapp
|
|
|
3080
3171
|
<div className="d-modal" data-align="top">
|
|
3081
3172
|
<div className="d-modal-backdrop" onClick={close} />
|
|
3082
3173
|
<div className="d-palette">
|
|
3083
|
-
<
|
|
3174
|
+
<div className="d-palette-search">
|
|
3175
|
+
<Search />
|
|
3176
|
+
<input className="d-palette-input" placeholder="Type a command..." />
|
|
3177
|
+
</div>
|
|
3084
3178
|
<ul className="d-palette-list">
|
|
3085
3179
|
<li className="d-palette-section">Navigation</li>
|
|
3086
3180
|
<li className="d-palette-row" data-active={i === selectedIndex}>
|
|
@@ -3562,19 +3656,48 @@ Dark themes emit stronger alpha values automatically.
|
|
|
3562
3656
|
| \`shimmer-skeleton\` | \`d-shimmer\` on skeleton placeholders during loading |
|
|
3563
3657
|
| \`zoom-pinch\` | Touch handlers (\`touchstart\`/\`touchmove\`) tracking pinch distance, OR \`gestureend\` on Safari; same scale transform as \`zoom-scroll\` |
|
|
3564
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
|
+
}
|
|
3565
3678
|
function generateDecantrMdV31(params) {
|
|
3566
3679
|
const template = loadTemplate("DECANTR.md.template");
|
|
3567
3680
|
const body = renderTemplate(template, {
|
|
3568
3681
|
GUARD_MODE: params.guardMode,
|
|
3569
3682
|
CSS_APPROACH: params.cssApproach,
|
|
3570
|
-
WORKFLOW_MODE: params.workflowMode === "brownfield-attach" ? "brownfield attach" : "greenfield scaffold",
|
|
3571
|
-
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.
|
|
3572
3685
|
|
|
3573
3686
|
Read \`.decantr/analysis.json\` first for the detected framework, routes, styling, layout, and dependency facts.
|
|
3574
3687
|
Then read \`.decantr/init-seed.json\` for the recommended attach defaults.
|
|
3575
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.
|
|
3576
3689
|
|
|
3577
|
-
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.
|
|
3578
3701
|
|
|
3579
3702
|
Treat the compiled execution-pack files as the primary source of truth.
|
|
3580
3703
|
Use narrative docs only as secondary explanation when the compiled packs are not enough.
|
|
@@ -3591,8 +3714,9 @@ Start implementation from the shell layouts and shared route structure before fi
|
|
|
3591
3714
|
const themeDesc = `${params.themeName || "default"} (${params.themeMode || "dark"} mode${params.themeShape ? `, ${params.themeShape} shape` : ""})`;
|
|
3592
3715
|
briefLines.push(`- **Theme:** ${themeDesc}`);
|
|
3593
3716
|
briefLines.push(
|
|
3594
|
-
`- **Workflow:** ${params.workflowMode
|
|
3717
|
+
`- **Workflow:** ${params.workflowMode || "greenfield-scaffold"}`
|
|
3595
3718
|
);
|
|
3719
|
+
briefLines.push(`- **Adoption mode:** ${params.adoptionMode || "decantr-css"}`);
|
|
3596
3720
|
if (params.personality && params.personality.length > 0) {
|
|
3597
3721
|
briefLines.push(`- **Personality:** ${params.personality.join(". ")}`);
|
|
3598
3722
|
}
|
|
@@ -3680,7 +3804,9 @@ function generateProjectJson(detected, options, registrySource) {
|
|
|
3680
3804
|
packageManager: detected.packageManager,
|
|
3681
3805
|
hasTypeScript: detected.hasTypeScript,
|
|
3682
3806
|
hasTailwind: detected.hasTailwind,
|
|
3683
|
-
existingRuleFiles: detected.existingRuleFiles
|
|
3807
|
+
existingRuleFiles: detected.existingRuleFiles,
|
|
3808
|
+
workspaceRoot: options.workspaceRoot || detected.projectRoot,
|
|
3809
|
+
appRoot: options.appRoot || detected.projectRoot
|
|
3684
3810
|
},
|
|
3685
3811
|
overrides: {
|
|
3686
3812
|
framework: options.target !== detected.framework ? options.target : null
|
|
@@ -3700,7 +3826,13 @@ function generateProjectJson(detected, options, registrySource) {
|
|
|
3700
3826
|
via: "cli",
|
|
3701
3827
|
version: CLI_VERSION,
|
|
3702
3828
|
flags: buildFlagsString(options),
|
|
3703
|
-
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)
|
|
3704
3836
|
}
|
|
3705
3837
|
};
|
|
3706
3838
|
if (options.blueprint) {
|
|
@@ -3714,6 +3846,12 @@ function buildFlagsString(options) {
|
|
|
3714
3846
|
if (options.theme) flags.push(`--theme=${options.theme}`);
|
|
3715
3847
|
if (options.mode) flags.push(`--mode=${options.mode}`);
|
|
3716
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
|
+
}
|
|
3717
3855
|
return flags.join(" ");
|
|
3718
3856
|
}
|
|
3719
3857
|
function generateTaskContextV3(templateName, essence) {
|
|
@@ -3772,7 +3910,8 @@ function generateScaffoldTaskContext(essence, scaffoldPack, manifest) {
|
|
|
3772
3910
|
const features = scaffoldPack.data.features.length > 0 ? scaffoldPack.data.features.join(", ") : "none";
|
|
3773
3911
|
const routePlan = scaffoldPack.data.routes.length > 0 ? scaffoldPack.data.routes.map((route) => {
|
|
3774
3912
|
const patternSummary = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
3775
|
-
|
|
3913
|
+
const pageLabel = route.sectionId ? `${route.sectionId}/${route.pageId}` : route.pageId;
|
|
3914
|
+
return `- \`${route.path}\` -> \`${pageLabel}\` [${patternSummary}]`;
|
|
3776
3915
|
}).join("\n") : "- No routes declared";
|
|
3777
3916
|
const successChecks = scaffoldPack.successChecks.map((check) => `- [${check.severity}] ${check.label}`).join("\n");
|
|
3778
3917
|
const tokenStrategy = scaffoldPack.tokenBudget.strategy.map((item) => `- ${item}`).join("\n");
|
|
@@ -3828,7 +3967,8 @@ function generateAddPageTaskContext(essence, scaffoldPack, manifest) {
|
|
|
3828
3967
|
}
|
|
3829
3968
|
const routePlan = scaffoldPack.data.routes.length > 0 ? scaffoldPack.data.routes.map((route) => {
|
|
3830
3969
|
const patternSummary = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
3831
|
-
|
|
3970
|
+
const pageLabel = route.sectionId ? `${route.sectionId}/${route.pageId}` : route.pageId;
|
|
3971
|
+
return `- \`${route.path}\` -> \`${pageLabel}\` [${patternSummary}]`;
|
|
3832
3972
|
}).join("\n") : "- No routes declared";
|
|
3833
3973
|
const sectionRefs = manifest?.sections.map(
|
|
3834
3974
|
(section) => `Section \`${section.id}\` -> \`.decantr/context/${section.markdown}\``
|
|
@@ -3884,7 +4024,8 @@ function generateModifyTaskContext(essence, scaffoldPack, manifest) {
|
|
|
3884
4024
|
}
|
|
3885
4025
|
const routePlan = scaffoldPack.data.routes.length > 0 ? scaffoldPack.data.routes.map((route) => {
|
|
3886
4026
|
const patternSummary = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
3887
|
-
|
|
4027
|
+
const pageLabel = route.sectionId ? `${route.sectionId}/${route.pageId}` : route.pageId;
|
|
4028
|
+
return `- \`${route.path}\` -> \`${pageLabel}\` [${patternSummary}]`;
|
|
3888
4029
|
}).join("\n") : "- No routes declared";
|
|
3889
4030
|
const pageRefs = manifest?.pages.map((page) => `Page \`${page.id}\` -> \`.decantr/context/${page.markdown}\``) ?? [];
|
|
3890
4031
|
const successChecks = scaffoldPack.successChecks.map((check) => `- [${check.severity}] ${check.label}`).join("\n");
|
|
@@ -4030,7 +4171,10 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
|
|
|
4030
4171
|
}
|
|
4031
4172
|
const refreshResult = await refreshDerivedFiles(projectRoot, essenceV3, registry, themeData, {
|
|
4032
4173
|
isInitialScaffold: true,
|
|
4033
|
-
patternSpecs
|
|
4174
|
+
patternSpecs,
|
|
4175
|
+
workflowMode: options.workflowMode,
|
|
4176
|
+
adoptionMode: options.adoptionMode,
|
|
4177
|
+
analysisArtifacts: options.analysisArtifacts
|
|
4034
4178
|
});
|
|
4035
4179
|
contextFiles.push(...refreshResult.contextFiles);
|
|
4036
4180
|
const gitignoreUpdated = updateGitignore(projectRoot);
|
|
@@ -4043,7 +4187,7 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
|
|
|
4043
4187
|
gitignoreUpdated
|
|
4044
4188
|
};
|
|
4045
4189
|
}
|
|
4046
|
-
function scaffoldMinimal(projectRoot) {
|
|
4190
|
+
function scaffoldMinimal(projectRoot, options = {}) {
|
|
4047
4191
|
const decantrDir = join2(projectRoot, ".decantr");
|
|
4048
4192
|
const customDir = join2(decantrDir, "custom");
|
|
4049
4193
|
const contentTypes = API_CONTENT_TYPES2;
|
|
@@ -4140,7 +4284,12 @@ function scaffoldMinimal(projectRoot) {
|
|
|
4140
4284
|
via: "cli",
|
|
4141
4285
|
version: CLI_VERSION,
|
|
4142
4286
|
flags: "--offline --minimal",
|
|
4143
|
-
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
|
|
4144
4293
|
}
|
|
4145
4294
|
};
|
|
4146
4295
|
const projectJsonPath = join2(decantrDir, "project.json");
|
|
@@ -4254,9 +4403,11 @@ function writeExecutionPackBundleArtifacts(contextDir, bundle) {
|
|
|
4254
4403
|
);
|
|
4255
4404
|
outputPaths.push(sectionPackPath);
|
|
4256
4405
|
}
|
|
4257
|
-
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`;
|
|
4258
4409
|
const pagePackPath = writeExecutionPackArtifacts(
|
|
4259
|
-
join2(contextDir,
|
|
4410
|
+
join2(contextDir, pageBaseName),
|
|
4260
4411
|
pagePack
|
|
4261
4412
|
);
|
|
4262
4413
|
outputPaths.push(pagePackPath);
|
|
@@ -4366,6 +4517,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4366
4517
|
let storedBlueprintId;
|
|
4367
4518
|
let storedVoice;
|
|
4368
4519
|
let storedWorkflowMode;
|
|
4520
|
+
let storedAdoptionMode;
|
|
4521
|
+
let storedAnalysisArtifacts = false;
|
|
4369
4522
|
const projectJsonFilePath = join2(decantrDir, "project.json");
|
|
4370
4523
|
let projectJsonData = {};
|
|
4371
4524
|
if (existsSync2(projectJsonFilePath)) {
|
|
@@ -4375,9 +4528,16 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4375
4528
|
if (projectJsonData.voice) storedVoice = projectJsonData.voice;
|
|
4376
4529
|
if (projectJsonData.initialized?.workflowMode)
|
|
4377
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;
|
|
4378
4535
|
} catch {
|
|
4379
4536
|
}
|
|
4380
4537
|
}
|
|
4538
|
+
const effectiveWorkflowMode = options?.workflowMode || storedWorkflowMode || "greenfield-scaffold";
|
|
4539
|
+
const effectiveAdoptionMode = options?.adoptionMode || storedAdoptionMode || "decantr-css";
|
|
4540
|
+
const effectiveAnalysisArtifacts = options?.analysisArtifacts ?? storedAnalysisArtifacts ?? false;
|
|
4381
4541
|
if (!storedVoice && storedBlueprintId) {
|
|
4382
4542
|
try {
|
|
4383
4543
|
const bpResult = await registry.fetchBlueprint(storedBlueprintId);
|
|
@@ -4401,7 +4561,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4401
4561
|
};
|
|
4402
4562
|
const personality = essence.dna.personality || [];
|
|
4403
4563
|
let themeData = prefetchedThemeData;
|
|
4404
|
-
|
|
4564
|
+
const shouldResolveThemeData = effectiveAdoptionMode !== "contract-only" || Boolean(themeData);
|
|
4565
|
+
if (!themeData && shouldResolveThemeData)
|
|
4405
4566
|
try {
|
|
4406
4567
|
const themeResult = await registry.fetchTheme(themeName);
|
|
4407
4568
|
if (themeResult?.data) {
|
|
@@ -4409,7 +4570,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4409
4570
|
}
|
|
4410
4571
|
} catch {
|
|
4411
4572
|
}
|
|
4412
|
-
|
|
4573
|
+
const registryIsOffline = typeof registry.isOffline === "function" ? registry.isOffline() : false;
|
|
4574
|
+
if (shouldResolveThemeData && !themeData?.seed?.primary && !registryIsOffline) {
|
|
4413
4575
|
try {
|
|
4414
4576
|
const apiUrl = registry.getApiUrl();
|
|
4415
4577
|
const resp = await fetch(`${apiUrl}/themes/@official/${themeName}`);
|
|
@@ -4439,7 +4601,10 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4439
4601
|
}
|
|
4440
4602
|
}
|
|
4441
4603
|
const stylesDir = join2(projectRoot, "src", "styles");
|
|
4442
|
-
|
|
4604
|
+
const shouldWriteCss = effectiveAdoptionMode !== "contract-only";
|
|
4605
|
+
if (shouldWriteCss) {
|
|
4606
|
+
mkdirSync2(stylesDir, { recursive: true });
|
|
4607
|
+
}
|
|
4443
4608
|
const densityLevel = essence.dna?.spacing?.density || "comfortable";
|
|
4444
4609
|
const spatialTokens = computeSpatialTokens(
|
|
4445
4610
|
densityLevel,
|
|
@@ -4450,8 +4615,9 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4450
4615
|
} : void 0
|
|
4451
4616
|
);
|
|
4452
4617
|
const tokensPath = join2(stylesDir, "tokens.css");
|
|
4618
|
+
const bridgePath = join2(stylesDir, "decantr-bridge.css");
|
|
4453
4619
|
const hasRealThemeData = themeData?.seed?.primary || themeData?.palette?.background;
|
|
4454
|
-
if (hasRealThemeData || !existsSync2(tokensPath)) {
|
|
4620
|
+
if (shouldWriteCss && (hasRealThemeData || !existsSync2(tokensPath))) {
|
|
4455
4621
|
if (themeData?.palette && mode && mode !== "auto") {
|
|
4456
4622
|
const paletteEntries = Object.values(themeData.palette);
|
|
4457
4623
|
const modeDefined = paletteEntries.some(
|
|
@@ -4486,27 +4652,36 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4486
4652
|
}
|
|
4487
4653
|
const features = essence.blueprint?.features ?? [];
|
|
4488
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") {
|
|
4489
4670
|
writeFileSync2(
|
|
4490
|
-
|
|
4491
|
-
|
|
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
|
+
`
|
|
4492
4678
|
);
|
|
4493
4679
|
}
|
|
4494
|
-
const treatmentsPath = join2(stylesDir, "treatments.css");
|
|
4495
|
-
let treatmentCSS = generateTreatmentCSS(
|
|
4496
|
-
spatialTokens,
|
|
4497
|
-
themeData?.treatments,
|
|
4498
|
-
themeData?.decorators,
|
|
4499
|
-
themeName,
|
|
4500
|
-
themeData?.decorator_definitions
|
|
4501
|
-
);
|
|
4502
|
-
const personalityCSS = generatePersonalityCSS(personality || [], themeData || {});
|
|
4503
|
-
treatmentCSS += personalityCSS;
|
|
4504
|
-
writeFileSync2(treatmentsPath, treatmentCSS);
|
|
4505
4680
|
const globalPath = join2(stylesDir, "global.css");
|
|
4506
|
-
if (!existsSync2(globalPath)) {
|
|
4681
|
+
if (shouldWriteCss && !existsSync2(globalPath)) {
|
|
4507
4682
|
writeFileSync2(globalPath, generateGlobalCSS(personality, essence));
|
|
4508
4683
|
}
|
|
4509
|
-
const cssFiles = [tokensPath, treatmentsPath, globalPath];
|
|
4684
|
+
const cssFiles = effectiveAdoptionMode === "contract-only" ? [] : effectiveAdoptionMode === "style-bridge" ? [tokensPath, bridgePath, globalPath] : [tokensPath, treatmentsPath, globalPath];
|
|
4510
4685
|
const earlyDecoratorList = [];
|
|
4511
4686
|
if (themeData?.decorators) {
|
|
4512
4687
|
for (const [name, desc] of Object.entries(themeData.decorators)) {
|
|
@@ -4535,8 +4710,10 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4535
4710
|
decantrMdPath,
|
|
4536
4711
|
generateDecantrMdV31({
|
|
4537
4712
|
guardMode,
|
|
4538
|
-
cssApproach:
|
|
4539
|
-
workflowMode:
|
|
4713
|
+
cssApproach: getCssApproachContent(effectiveAdoptionMode),
|
|
4714
|
+
workflowMode: effectiveWorkflowMode,
|
|
4715
|
+
adoptionMode: effectiveAdoptionMode,
|
|
4716
|
+
analysisArtifacts: effectiveAnalysisArtifacts,
|
|
4540
4717
|
blueprintId: storedBlueprintId || getLegacyBlueprintId(essence.meta) || void 0,
|
|
4541
4718
|
themeName,
|
|
4542
4719
|
themeMode: mode,
|