@decantr/cli 1.7.24 → 1.7.26

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-7H3PAC5Y.js";
3
- import "./chunk-RH6IQ7YX.js";
2
+ import "./chunk-RAAUNHXD.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
@@ -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
- <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>
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 **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.
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 === "brownfield-attach" ? "brownfield attach" : "greenfield scaffold"}`
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
- return `- \`${route.path}\` -> \`${route.pageId}\` [${patternSummary}]`;
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
- return `- \`${route.path}\` -> \`${route.pageId}\` [${patternSummary}]`;
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
- return `- \`${route.path}\` -> \`${route.pageId}\` [${patternSummary}]`;
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-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
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, `page-${pagePack.data.pageId}-pack`),
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
- if (!themeData)
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
- if (!themeData?.seed?.primary) {
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
- mkdirSync2(stylesDir, { recursive: true });
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
- tokensPath,
4494
- 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
+ `
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: CSS_APPROACH_CONTENT,
4542
- workflowMode: storedWorkflowMode,
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,