@decantr/cli 1.7.6 → 1.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,12 +14,22 @@ npm install -D @decantr/cli
14
14
  Or run it without installing:
15
15
 
16
16
  ```bash
17
- npx @decantr/cli init --blueprint=agent-marketplace --yes
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.
21
+ Use `decantr analyze` first when you already have an app and want Decantr governance without adopting a blueprint.
22
+ Use `decantr init` to attach Decantr contract/context files to an existing project or to create a contract-only workspace.
23
+
24
+ Current starter adapter availability:
25
+
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
28
+
20
29
  ## What It Does
21
30
 
22
31
  - scaffolds Decantr projects from blueprints, archetypes, or prompts
32
+ - supports three workflow lanes: greenfield blueprint, brownfield adoption, and hybrid composition
23
33
  - generates execution-pack context files for AI coding assistants
24
34
  - audits projects against Decantr contracts
25
35
  - searches the registry and showcase benchmark corpus
@@ -28,7 +38,10 @@ npx @decantr/cli init --blueprint=agent-marketplace --yes
28
38
  ## Common Commands
29
39
 
30
40
  ```bash
31
- decantr init --blueprint=agent-marketplace
41
+ decantr new my-app --blueprint=agent-marketplace
42
+ decantr analyze
43
+ decantr init --existing --yes
44
+ decantr init --existing --blueprint=agent-marketplace
32
45
  decantr magic "AI-native analytics workspace"
33
46
  decantr audit
34
47
  decantr check
@@ -36,14 +49,49 @@ decantr registry summary --namespace @official --json
36
49
  decantr showcase verification --json
37
50
  ```
38
51
 
52
+ ## Greenfield Certification
53
+
54
+ Use the built-in certification harness before releases when you want to prove that representative blueprints still scaffold into runnable starter projects:
55
+
56
+ ```bash
57
+ pnpm --filter @decantr/cli certify:blueprints
58
+ ```
59
+
60
+ By default it certifies `portfolio`, `producer-studio`, and `agent-marketplace` by:
61
+
62
+ - running `decantr new` in fresh temp directories
63
+ - seeding offline content from `DECANTR_CONTENT_DIR` or a sibling `decantr-content` checkout
64
+ - verifying the starter runtime files and router mode match the generated essence
65
+ - running `npm run build` in each scaffolded project
66
+
67
+ Override the matrix or emit JSON when needed:
68
+
69
+ ```bash
70
+ pnpm --filter @decantr/cli certify:blueprints -- --blueprints=portfolio,legal-research --json
71
+ ```
72
+
39
73
  Offline blueprint scaffolding expects a real local content source:
40
74
 
41
75
  ```bash
42
- DECANTR_CONTENT_DIR=/path/to/decantr-content decantr init --blueprint=agent-marketplace --offline --yes
76
+ DECANTR_CONTENT_DIR=/path/to/decantr-content decantr new my-app --blueprint=agent-marketplace --offline
43
77
  ```
44
78
 
45
79
  If a requested offline blueprint, archetype, or theme cannot be resolved from local cache/custom content or `DECANTR_CONTENT_DIR`, the CLI now stops explicitly instead of silently falling back to the default scaffold.
46
80
 
81
+ ## Workflow Certification
82
+
83
+ The broader workflow matrix now has its own certification entrypoint:
84
+
85
+ ```bash
86
+ pnpm --filter @decantr/cli certify:workflows
87
+ ```
88
+
89
+ It covers:
90
+
91
+ - greenfield blueprint bootstrap
92
+ - brownfield `analyze -> init --existing`
93
+ - hybrid follow-up composition via Decantr mutation commands
94
+
47
95
  ## Generated Context
48
96
 
49
97
  Scaffolded projects include compiled execution packs under `.decantr/context/`, including:
package/dist/bin.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-KAEQTVAM.js";
3
- import "./chunk-H4H3IQJK.js";
2
+ import "./chunk-ODJQZXHQ.js";
3
+ import "./chunk-74G2RQDO.js";
4
4
  import "./chunk-KUDAVJOR.js";
@@ -6,8 +6,9 @@ import { computeSpatialTokens } from "@decantr/essence-spec";
6
6
  import { compileExecutionPackBundle } from "@decantr/core";
7
7
 
8
8
  // src/treatments.ts
9
- function generateTreatmentCSS(spatialTokens, treatmentOverrides, themeDecorators, themeName) {
9
+ function generateTreatmentCSS(spatialTokens, treatmentOverrides, themeDecorators, themeName, themeDecoratorDefinitions) {
10
10
  const lines = [];
11
+ const decoratorAnimationNames = /* @__PURE__ */ new Set();
11
12
  lines.push("/* Generated by @decantr/cli \u2014 Visual Treatment System */");
12
13
  lines.push("");
13
14
  lines.push("@layer treatments {");
@@ -195,9 +196,9 @@ ${themeBody}
195
196
  ["font-size", "0.75rem"],
196
197
  ["font-weight", "500"],
197
198
  ["padding", "0.125rem 0.5rem"],
198
- ["margin-top", "calc(var(--d-annotation-mt) * var(--d-density-scale, 1))"],
199
199
  ["border-radius", "var(--d-radius-full)"],
200
- ["background", "var(--d-surface)"],
200
+ ["background", "color-mix(in srgb, var(--d-surface-raised) 88%, transparent)"],
201
+ ["border", "1px solid color-mix(in srgb, var(--d-border) 72%, transparent)"],
201
202
  ["color", "var(--d-text-muted)"],
202
203
  ["white-space", "nowrap"]
203
204
  ]);
@@ -247,10 +248,47 @@ ${themeBody}
247
248
  lines.push("}");
248
249
  lines.push("");
249
250
  lines.push("} /* end @layer treatments */");
251
+ const decoratorRules = [];
252
+ if (themeDecoratorDefinitions) {
253
+ for (const [className, definition] of Object.entries(themeDecoratorDefinitions)) {
254
+ const props = definition?.suggested_properties ?? {};
255
+ const entries = Object.entries(props);
256
+ if (entries.length === 0) continue;
257
+ decoratorRules.push(`.${className} {`);
258
+ for (const [prop, value] of entries) {
259
+ decoratorRules.push(` ${prop}: ${value};`);
260
+ if (prop === "animation") {
261
+ const animationName = value.split(/\s+/)[0]?.trim();
262
+ if (animationName) decoratorAnimationNames.add(animationName);
263
+ }
264
+ }
265
+ decoratorRules.push("}");
266
+ decoratorRules.push("");
267
+ }
268
+ }
269
+ const decoratorKeyframes = [];
270
+ if (decoratorAnimationNames.has("carbon-fade-slide")) {
271
+ decoratorKeyframes.push("@keyframes carbon-fade-slide {");
272
+ decoratorKeyframes.push(" from { opacity: 0; transform: translateY(12px); }");
273
+ decoratorKeyframes.push(" to { opacity: 1; transform: translateY(0); }");
274
+ decoratorKeyframes.push("}");
275
+ decoratorKeyframes.push("");
276
+ }
277
+ if (decoratorAnimationNames.has("pulse")) {
278
+ decoratorKeyframes.push("@keyframes pulse {");
279
+ decoratorKeyframes.push(" 0%, 100% { opacity: 1; }");
280
+ decoratorKeyframes.push(" 50% { opacity: 0.5; }");
281
+ decoratorKeyframes.push("}");
282
+ decoratorKeyframes.push("");
283
+ }
284
+ const decoratorComments = themeDecorators ? Object.entries(themeDecorators).map(([name, description]) => ` /* .${name}: ${description} */`).join("\n") : " /* No theme decorators available. */";
285
+ const decoratorBody = decoratorRules.length > 0 ? `${decoratorRules.join("\n")}${decoratorKeyframes.length > 0 ? `
286
+ ${decoratorKeyframes.join("\n")}` : ""}${decoratorComments ? `
287
+ ${decoratorComments}` : ""}` : `${decoratorComments}
288
+ /* Canonical decorator CSS should be derived from theme decorator definitions when available. */`;
250
289
  const decoratorBlock = `
251
290
  @layer decorators {
252
- /* Decorator CSS is AI-generated from structured definitions in section context files. */
253
- /* See .decantr/context/section-*.md for intent, suggested properties, and usage guidance. */
291
+ ${decoratorBody}
254
292
  }
255
293
  `;
256
294
  lines.push(decoratorBlock);
@@ -598,6 +636,7 @@ function mapRegistryThemeToThemeData(theme) {
598
636
  typography: theme.typography,
599
637
  motion: theme.motion,
600
638
  decorators: theme.decorators,
639
+ decorator_definitions: theme.decorator_definitions,
601
640
  treatments: theme.treatments,
602
641
  spatial: theme.spatial,
603
642
  radius: theme.radius,
@@ -777,11 +816,15 @@ body {
777
816
  transform: translateY(0);
778
817
  }
779
818
 
780
- img, picture, video, canvas, svg {
819
+ img, picture, video, canvas {
781
820
  display: block;
782
821
  max-width: 100%;
783
822
  }
784
823
 
824
+ svg {
825
+ max-width: 100%;
826
+ }
827
+
785
828
  input, button, textarea, select {
786
829
  font: inherit;
787
830
  color: inherit;
@@ -1009,10 +1052,14 @@ import './styles/global.css'; // Resets
1009
1052
  ### Runtime Rules
1010
1053
 
1011
1054
  - Use the real \`@decantr/css\` runtime for atoms. If \`package.json\` does not already depend on \`@decantr/css\`, add it before building.
1055
+ - If \`package.json\`, app entry files, or router/runtime files are absent, create them explicitly for the declared target instead of assuming a hidden starter already exists.
1012
1056
  - Do **not** create local atom-runtime substitutes such as \`src/lib/css.js\`, \`src/lib/css.ts\`, or hand-written \`src/styles/atoms.css\` files unless the task explicitly asks for a fallback runtime.
1013
1057
  - Keep atoms in \`css(...)\`, treatments as semantic classes, and theme decorators as additive classes. Do not blur those roles together.
1058
+ - Do **not** use inline visual style values or component-scoped \`<style>\` tags as the primary styling path. Colors, spacing, borders, shadows, gradients, and transitions should come from atoms, treatments, decorators, or CSS variables. Inline styles are only acceptable for truly dynamic geometry that cannot be expressed through the contract.
1014
1059
  - Use \`d-control\` as the default semantic treatment for inputs, selects, and textareas. Theme decorators such as \`carbon-input\` are additive and should only layer on when the section or theme contract explicitly calls for them.
1015
1060
  - Use loading decorators such as \`carbon-skeleton\` as optional enhancement on top of a structurally correct loading state \u2014 they do not replace the need for a real loading/skeleton branch.
1061
+ - Shells own spacing, centering, and scroll containers. Pages should not duplicate shell responsibilities with extra full-height wrappers, max-width wrappers, or page-local padding unless the route contract explicitly requires it.
1062
+ - If a required decorator class is referenced in the generated contract but missing from generated CSS, report that contract gap instead of inventing a parallel visual system.
1016
1063
 
1017
1064
  ### Visual Treatments
1018
1065
 
@@ -1044,12 +1091,19 @@ Atoms + treatment + theme decorator:
1044
1091
 
1045
1092
  \`\`\`tsx
1046
1093
  // Responsive prefix \u2014 applies at breakpoint and above:
1047
- css('_col sm:_row')
1094
+ css('_col _sm:row')
1048
1095
 
1049
1096
  // Pseudo prefix:
1050
- css('hover:_opacity80')
1097
+ css('_bgprimary _h:bgprimary/80')
1051
1098
  \`\`\`
1052
1099
 
1100
+ ### Prefix and Arbitrary Value Syntax
1101
+
1102
+ - Responsive prefixes are part of the atom token itself: \`_sm:gc2\`, \`_md:flex\`, \`_lg:row\`.
1103
+ - Pseudo prefixes are also token-prefixed: \`_h:bgprimary/80\`, \`_f:borderprimary\`, \`_fv:shadowmd\`.
1104
+ - Arbitrary values use square brackets when the standard scale is not enough: \`_w[512px]\`, \`_h[100vh]\`, \`_p[clamp(1rem,3vw,2rem)]\`, \`_z[40]\`.
1105
+ - When you see bracket atoms in shell or page contracts, treat them as first-class Decantr syntax, not as an error or a cue to fall back to inline styles.
1106
+
1053
1107
  ### Atom Reference
1054
1108
 
1055
1109
  #### Display
@@ -1226,7 +1280,7 @@ css('hover:_opacity80')
1226
1280
  | \`_trans\` | \`transition:all 0.15s ease\` |
1227
1281
  | \`_visible\`, \`_invisible\` | visibility |
1228
1282
 
1229
- Responsive prefixes: \`_sm:\`, \`_md:\`, \`_lg:\` (e.g. \`_md:gc2\`, \`_lg:gc4\`, \`_sm:flex\`).
1283
+ Responsive prefixes: \`_sm:\`, \`_md:\`, \`_lg:\`, \`_xl:\` (e.g. \`_sm:gc2\`, \`_md:flex\`, \`_lg:row\`).
1230
1284
 
1231
1285
  ### Section Labels
1232
1286
 
@@ -1245,6 +1299,7 @@ If the theme provides motion tokens, apply the \`entrance-fade\` class to page c
1245
1299
  ### Navigation Shortcuts
1246
1300
 
1247
1301
  If the essence defines hotkeys or command_palette, implement as keyboard event listeners (useEffect + keydown) \u2014 not as visible UI text.
1302
+ Missing declared navigation features are contract drift, not optional polish.
1248
1303
 
1249
1304
  ### Design Tokens
1250
1305
 
@@ -1320,7 +1375,23 @@ function generateDecantrMdV31(params) {
1320
1375
  const template = loadTemplate("DECANTR.md.template");
1321
1376
  const body = renderTemplate(template, {
1322
1377
  GUARD_MODE: params.guardMode,
1323
- CSS_APPROACH: params.cssApproach
1378
+ CSS_APPROACH: params.cssApproach,
1379
+ WORKFLOW_MODE: params.workflowMode === "brownfield-attach" ? "brownfield attach" : "greenfield scaffold",
1380
+ WORKFLOW_GUIDANCE: params.workflowMode === "brownfield-attach" ? `This project is using Decantr in **brownfield attach** mode.
1381
+
1382
+ Read \`.decantr/analysis.json\` first for the detected framework, routes, styling, layout, and dependency facts.
1383
+ Then read \`.decantr/init-seed.json\` for the recommended attach defaults.
1384
+ Then read \`.decantr/context/scaffold-pack.md\` and \`.decantr/context/scaffold.md\` to understand the Decantr contract you are layering onto the existing app.
1385
+
1386
+ Preserve the current framework, package manager, router, and working runtime structure unless the contract gives you a reviewed reason to change them. Map existing routes and components onto the declared Decantr sections/pages before creating new files. Registry content is optional in this workflow unless the task explicitly asks for it.` : `This project is using Decantr in **greenfield scaffold** mode.
1387
+
1388
+ Treat the compiled execution-pack files as the primary source of truth.
1389
+ Use narrative docs only as secondary explanation when the compiled packs are not enough.
1390
+ Use only files present in this workspace as the source of truth. If local scaffold files disagree, stop and report the mismatch instead of relying on external Decantr assumptions or prior examples.
1391
+
1392
+ Read \`.decantr/context/scaffold-pack.md\` first for the compact compiled shell, theme, feature, and route contract.
1393
+ Then read \`.decantr/context/scaffold.md\` for the fuller app overview, topology, route map, and voice guidance.
1394
+ Start implementation from the shell layouts and shared route structure before filling in section pages.`
1324
1395
  });
1325
1396
  const briefLines = [];
1326
1397
  briefLines.push("## Project Brief");
@@ -1328,6 +1399,7 @@ function generateDecantrMdV31(params) {
1328
1399
  briefLines.push(`- **Blueprint:** ${params.blueprintId || "custom"}`);
1329
1400
  const themeDesc = `${params.themeName || "default"} (${params.themeMode || "dark"} mode${params.themeShape ? `, ${params.themeShape} shape` : ""})`;
1330
1401
  briefLines.push(`- **Theme:** ${themeDesc}`);
1402
+ briefLines.push(`- **Workflow:** ${params.workflowMode === "brownfield-attach" ? "brownfield attach" : "greenfield scaffold"}`);
1331
1403
  if (params.personality && params.personality.length > 0) {
1332
1404
  briefLines.push(`- **Personality:** ${params.personality.join(". ")}`);
1333
1405
  }
@@ -1414,7 +1486,8 @@ function generateProjectJson(detected, options, registrySource) {
1414
1486
  at: now,
1415
1487
  via: "cli",
1416
1488
  version: CLI_VERSION,
1417
- flags: buildFlagsString(options)
1489
+ flags: buildFlagsString(options),
1490
+ workflowMode: options.workflowMode || "greenfield-scaffold"
1418
1491
  }
1419
1492
  };
1420
1493
  if (options.blueprint) {
@@ -1854,7 +1927,8 @@ function scaffoldMinimal(projectRoot) {
1854
1927
  at: now,
1855
1928
  via: "cli",
1856
1929
  version: CLI_VERSION,
1857
- flags: "--offline --minimal"
1930
+ flags: "--offline --minimal",
1931
+ workflowMode: "greenfield-scaffold"
1858
1932
  }
1859
1933
  };
1860
1934
  const projectJsonPath = join(decantrDir, "project.json");
@@ -2054,6 +2128,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
2054
2128
  mkdirSync(contextDir, { recursive: true });
2055
2129
  let storedBlueprintId;
2056
2130
  let storedVoice;
2131
+ let storedWorkflowMode;
2057
2132
  const projectJsonFilePath = join(decantrDir, "project.json");
2058
2133
  let projectJsonData = {};
2059
2134
  if (existsSync(projectJsonFilePath)) {
@@ -2061,6 +2136,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
2061
2136
  projectJsonData = JSON.parse(readFileSync(projectJsonFilePath, "utf-8"));
2062
2137
  if (projectJsonData.blueprintId) storedBlueprintId = projectJsonData.blueprintId;
2063
2138
  if (projectJsonData.voice) storedVoice = projectJsonData.voice;
2139
+ if (projectJsonData.initialized?.workflowMode) storedWorkflowMode = projectJsonData.initialized.workflowMode;
2064
2140
  } catch {
2065
2141
  }
2066
2142
  }
@@ -2141,7 +2217,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
2141
2217
  spatialTokens,
2142
2218
  themeData?.treatments,
2143
2219
  themeData?.decorators,
2144
- themeName
2220
+ themeName,
2221
+ themeData?.decorator_definitions
2145
2222
  );
2146
2223
  const personalityCSS = generatePersonalityCSS(personality || [], themeData || {});
2147
2224
  treatmentCSS += personalityCSS;
@@ -2178,6 +2255,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
2178
2255
  writeFileSync(decantrMdPath, generateDecantrMdV31({
2179
2256
  guardMode,
2180
2257
  cssApproach: CSS_APPROACH_CONTENT,
2258
+ workflowMode: storedWorkflowMode,
2181
2259
  blueprintId: storedBlueprintId || getLegacyBlueprintId(essence.meta) || void 0,
2182
2260
  themeName,
2183
2261
  themeMode: mode,
@@ -3112,9 +3190,18 @@ function generateScaffoldContext(input) {
3112
3190
  lines.push("");
3113
3191
  if (navigation.command_palette) {
3114
3192
  lines.push("- Command palette: enabled");
3193
+ lines.push("- Requirement: implement a real keyboard-triggered command palette, not just placeholder UI text.");
3115
3194
  }
3116
3195
  if (navigation.hotkeys && navigation.hotkeys.length > 0) {
3117
3196
  lines.push(`- Hotkeys: ${navigation.hotkeys.length} configured`);
3197
+ for (const hotkey of navigation.hotkeys) {
3198
+ if (typeof hotkey === "object" && hotkey !== null && typeof hotkey.key === "string") {
3199
+ const target = [hotkey.label, hotkey.route || hotkey.action].filter(Boolean).join(" \u2014 ");
3200
+ lines.push(` - \`${hotkey.key}\`${target ? `: ${target}` : ""}`);
3201
+ }
3202
+ }
3203
+ lines.push("- Requirement: implement these bindings as real keyboard shortcuts, not as decorative text.");
3204
+ lines.push("- Presentation rule: do not append hotkey text to persistent nav labels, breadcrumbs, or page titles unless the shell or route contract explicitly requests visible shortcut hints.");
3118
3205
  }
3119
3206
  lines.push("");
3120
3207
  }