@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
- - other contract targets remain valid Decantr targets, but `decantr new` will keep them in contract-only mode until their adapters land
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 three workflow lanes: greenfield blueprint, brownfield adoption, and hybrid composition
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
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-2WQLERIZ.js";
3
- import "./chunk-SN7C63TF.js";
2
+ import "./chunk-7FXMRAC3.js";
3
+ import "./chunk-3K6HWLD5.js";
4
4
  import "./chunk-QRQCPD3C.js";
@@ -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
- ["padding", "1rem 1.25rem"],
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", "1rem"],
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
- ["padding", "0.5rem 0.75rem"],
1354
- ["border-radius", "var(--d-radius-sm)"],
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-size", "0.875rem"],
1358
- ["transition", "background 0.1s ease"]
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
- var CLI_VERSION = "1.0.0";
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;${spatialLines2}
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 _items-center')}\` |
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('_w-full _maxw[40rem]')}\` |
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 _t0')}\` |
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
- <input className="d-palette-input" placeholder="Type a command..." />
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 **greenfield scaffold** mode.
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 === "brownfield-attach" ? "brownfield attach" : "greenfield scaffold"}`
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
- return `- \`${route.path}\` -> \`${route.pageId}\` [${patternSummary}]`;
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
- return `- \`${route.path}\` -> \`${route.pageId}\` [${patternSummary}]`;
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
- return `- \`${route.path}\` -> \`${route.pageId}\` [${patternSummary}]`;
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-scaffold"
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, `page-${pagePack.data.pageId}-pack`),
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
- if (!themeData)
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
- if (!themeData?.seed?.primary) {
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
- mkdirSync2(stylesDir, { recursive: true });
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
- tokensPath,
4491
- generateTokensCSS(themeData, mode, spatialTokens, { hasThemeToggle })
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: CSS_APPROACH_CONTENT,
4539
- workflowMode: storedWorkflowMode,
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,