@decantr/cli 2.3.1 → 2.4.0

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.
@@ -14,7 +14,7 @@ import {
14
14
  scaffoldProject,
15
15
  syncRegistry,
16
16
  writeExecutionPackBundleArtifacts
17
- } from "./chunk-WDA4SHIQ.js";
17
+ } from "./chunk-PEGMSXDJ.js";
18
18
  import {
19
19
  buildGuardRegistryContext,
20
20
  createDoctrineMap,
@@ -247,119 +247,42 @@ function getApiKeyOrToken() {
247
247
  import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
248
248
  import { basename, join as join3 } from "path";
249
249
  import { resolvePackAdapter } from "@decantr/core";
250
- var reactViteBootstrapAdapter = {
251
- id: "react-vite",
252
- label: "React + Vite starter",
253
- target: "react",
254
- packAdapter: "react-vite",
255
- capabilities: {
256
- bootstrap: true,
257
- realize: true,
258
- attach: true,
259
- styling: true,
260
- verify: true
261
- },
262
- attach: {
263
- routeRoots: ["src/App.tsx", "src/routes", "src/pages"],
264
- layoutFiles: ["src/App.tsx", "src/main.tsx"],
265
- componentRoots: ["src/components", "src/pages", "src/routes"]
266
- },
267
- verify: {
268
- devCommand: "npm run dev",
269
- buildCommand: "npm run build",
270
- distDir: "dist"
271
- },
272
- writeProjectFiles(projectDir, title, routingMode) {
273
- const srcDir = join3(projectDir, "src");
274
- const routerImport = routingMode === "hash" ? "HashRouter" : "BrowserRouter";
275
- const packageJson = {
276
- name: basename(projectDir) || "decantr-app",
277
- private: true,
278
- version: "0.0.0",
279
- type: "module",
280
- scripts: {
281
- dev: "vite",
282
- build: "tsc -b && vite build",
283
- preview: "vite preview"
284
- },
285
- dependencies: {
286
- react: "^19.0.0",
287
- "react-dom": "^19.0.0",
288
- "react-router-dom": "^7.0.0",
289
- "@decantr/css": "^1.0.4",
290
- // P0-4: Lucide is the canonical icon library Decantr blueprints
291
- // reference in personality prose ("Lucide icons"). Including it by
292
- // default means cold scaffolds don't have to hand-roll inline SVGs.
293
- // Tree-shaking eliminates unused imports, so the bundle cost is
294
- // zero when unused and ~2KB per icon when used.
295
- "lucide-react": "^0.468.0"
296
- },
297
- devDependencies: {
298
- "@types/react": "^19.0.0",
299
- "@types/react-dom": "^19.0.0",
300
- "@vitejs/plugin-react": "^4.0.0",
301
- typescript: "^5.7.0",
302
- vite: "^6.0.0"
303
- }
304
- };
305
- writeFileSync3(join3(projectDir, "package.json"), JSON.stringify(packageJson, null, 2) + "\n");
306
- const viteConfig = `import { defineConfig } from 'vite';
307
- import react from '@vitejs/plugin-react';
308
-
309
- export default defineConfig({
310
- plugins: [react()],
311
- });
312
- `;
313
- writeFileSync3(join3(projectDir, "vite.config.ts"), viteConfig);
314
- const tsconfig = {
315
- compilerOptions: {
316
- target: "ES2020",
317
- useDefineForClassFields: true,
318
- lib: ["ES2020", "DOM", "DOM.Iterable"],
319
- module: "ESNext",
320
- skipLibCheck: true,
321
- moduleResolution: "bundler",
322
- allowImportingTsExtensions: true,
323
- isolatedModules: true,
324
- moduleDetection: "force",
325
- noEmit: true,
326
- jsx: "react-jsx",
327
- strict: true,
328
- noUnusedLocals: true,
329
- noUnusedParameters: true,
330
- noFallthroughCasesInSwitch: true,
331
- noUncheckedSideEffectImports: true
332
- },
333
- include: ["src"]
334
- };
335
- writeFileSync3(join3(projectDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n");
336
- const tsconfigApp = {
337
- compilerOptions: {
338
- tsBuildInfoFile: "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
339
- target: "ES2020",
340
- useDefineForClassFields: true,
341
- lib: ["ES2020", "DOM", "DOM.Iterable"],
342
- module: "ESNext",
343
- skipLibCheck: true,
344
- moduleResolution: "bundler",
345
- allowImportingTsExtensions: true,
346
- isolatedModules: true,
347
- moduleDetection: "force",
348
- noEmit: true,
349
- jsx: "react-jsx",
350
- strict: true,
351
- noUnusedLocals: true,
352
- noUnusedParameters: true,
353
- noFallthroughCasesInSwitch: true,
354
- noUncheckedSideEffectImports: true
355
- },
356
- include: ["src"]
357
- };
358
- writeFileSync3(
359
- join3(projectDir, "tsconfig.app.json"),
360
- JSON.stringify(tsconfigApp, null, 2) + "\n"
361
- );
362
- const indexHtml = `<!doctype html>
250
+ function writeJson(path, value) {
251
+ writeFileSync3(path, JSON.stringify(value, null, 2) + "\n");
252
+ }
253
+ function mkdirp(path) {
254
+ mkdirSync3(path, { recursive: true });
255
+ }
256
+ function starterName(projectDir, fallback) {
257
+ return basename(projectDir) || fallback;
258
+ }
259
+ function writeViteTsConfig(projectDir, options = {}) {
260
+ writeJson(join3(projectDir, "tsconfig.json"), {
261
+ compilerOptions: {
262
+ target: "ES2020",
263
+ useDefineForClassFields: true,
264
+ lib: ["ES2020", "DOM", "DOM.Iterable"],
265
+ module: "ESNext",
266
+ skipLibCheck: true,
267
+ moduleResolution: "bundler",
268
+ allowImportingTsExtensions: true,
269
+ isolatedModules: true,
270
+ moduleDetection: "force",
271
+ noEmit: true,
272
+ ...options.jsx ? { jsx: options.jsx } : {},
273
+ strict: true,
274
+ noUnusedLocals: true,
275
+ noUnusedParameters: true,
276
+ noFallthroughCasesInSwitch: true,
277
+ noUncheckedSideEffectImports: true
278
+ },
279
+ include: ["src"]
280
+ });
281
+ }
282
+ function writeIndexHtml(projectDir, title, entry) {
283
+ writeFileSync3(
284
+ join3(projectDir, "index.html"),
285
+ `<!doctype html>
363
286
  <html lang="en">
364
287
  <head>
365
288
  <meta charset="UTF-8" />
@@ -368,13 +291,186 @@ export default defineConfig({
368
291
  </head>
369
292
  <body>
370
293
  <div id="root"></div>
371
- <script type="module" src="/src/main.tsx"></script>
294
+ <script type="module" src="${entry}"></script>
372
295
  </body>
373
296
  </html>
374
- `;
375
- writeFileSync3(join3(projectDir, "index.html"), indexHtml);
376
- mkdirSync3(srcDir, { recursive: true });
377
- const mainTsx = `import { StrictMode } from 'react';
297
+ `
298
+ );
299
+ }
300
+ function ensureStyleDir(projectDir) {
301
+ mkdirp(join3(projectDir, "src", "styles"));
302
+ }
303
+ function packagePlanPackageJson(projectDir, fallbackName, packagePlan) {
304
+ return {
305
+ name: starterName(projectDir, fallbackName),
306
+ private: true,
307
+ version: "0.0.0",
308
+ type: "module",
309
+ scripts: packagePlan.scripts,
310
+ dependencies: packagePlan.dependencies,
311
+ devDependencies: packagePlan.devDependencies
312
+ };
313
+ }
314
+ var reactVitePackagePlan = {
315
+ scripts: {
316
+ dev: "vite",
317
+ build: "tsc -b && vite build",
318
+ preview: "vite preview"
319
+ },
320
+ dependencies: {
321
+ react: "^19.0.0",
322
+ "react-dom": "^19.0.0",
323
+ "react-router-dom": "^7.0.0",
324
+ "@decantr/css": "^1.0.4",
325
+ "lucide-react": "^0.468.0"
326
+ },
327
+ devDependencies: {
328
+ "@types/react": "^19.0.0",
329
+ "@types/react-dom": "^19.0.0",
330
+ "@vitejs/plugin-react": "^4.0.0",
331
+ typescript: "^5.7.0",
332
+ vite: "^6.0.0"
333
+ }
334
+ };
335
+ var nextPackagePlan = {
336
+ scripts: {
337
+ dev: "next dev",
338
+ build: "next build",
339
+ start: "next start"
340
+ },
341
+ dependencies: {
342
+ "@decantr/css": "^1.0.4",
343
+ "lucide-react": "^0.468.0",
344
+ next: "^16.0.0",
345
+ react: "^19.0.0",
346
+ "react-dom": "^19.0.0"
347
+ },
348
+ devDependencies: {
349
+ "@types/node": "^20.0.0",
350
+ "@types/react": "^19.0.0",
351
+ "@types/react-dom": "^19.0.0",
352
+ typescript: "^5.7.0"
353
+ }
354
+ };
355
+ var vanillaPackagePlan = {
356
+ scripts: {
357
+ dev: "vite",
358
+ build: "vite build",
359
+ preview: "vite preview"
360
+ },
361
+ dependencies: {
362
+ "@decantr/css": "^1.0.4",
363
+ lucide: "^0.468.0"
364
+ },
365
+ devDependencies: {
366
+ vite: "^6.0.0"
367
+ }
368
+ };
369
+ var vuePackagePlan = {
370
+ scripts: {
371
+ dev: "vite",
372
+ build: "vue-tsc -b && vite build",
373
+ preview: "vite preview"
374
+ },
375
+ dependencies: {
376
+ "@decantr/css": "^1.0.4",
377
+ "@vitejs/plugin-vue": "^5.0.0",
378
+ "@vue/compiler-sfc": "^3.5.0",
379
+ "lucide-vue-next": "^0.468.0",
380
+ vue: "^3.5.0",
381
+ "vue-router": "^4.4.0"
382
+ },
383
+ devDependencies: {
384
+ typescript: "^5.7.0",
385
+ vite: "^6.0.0",
386
+ "vue-tsc": "^2.1.0"
387
+ }
388
+ };
389
+ var sveltePackagePlan = {
390
+ scripts: {
391
+ dev: "vite dev",
392
+ build: "vite build",
393
+ preview: "vite preview"
394
+ },
395
+ dependencies: {
396
+ "@decantr/css": "^1.0.4",
397
+ "@sveltejs/adapter-auto": "^3.0.0",
398
+ "@sveltejs/kit": "^2.0.0",
399
+ "@sveltejs/vite-plugin-svelte": "^5.0.0",
400
+ lucide: "^0.468.0",
401
+ svelte: "^5.0.0"
402
+ },
403
+ devDependencies: {
404
+ typescript: "^5.7.0",
405
+ vite: "^6.0.0"
406
+ }
407
+ };
408
+ var angularPackagePlan = {
409
+ scripts: {
410
+ dev: "ng serve",
411
+ build: "ng build",
412
+ start: "ng serve"
413
+ },
414
+ dependencies: {
415
+ "@angular/animations": "^19.0.0",
416
+ "@angular/common": "^19.0.0",
417
+ "@angular/compiler": "^19.0.0",
418
+ "@angular/core": "^19.0.0",
419
+ "@angular/forms": "^19.0.0",
420
+ "@angular/platform-browser": "^19.0.0",
421
+ "@angular/router": "^19.0.0",
422
+ "@decantr/css": "^1.0.4",
423
+ lucide: "^0.468.0",
424
+ rxjs: "^7.8.0",
425
+ tslib: "^2.8.0",
426
+ "zone.js": "^0.15.0"
427
+ },
428
+ devDependencies: {
429
+ "@angular-devkit/build-angular": "^19.0.0",
430
+ "@angular/cli": "^19.0.0",
431
+ "@angular/compiler-cli": "^19.0.0",
432
+ typescript: "^5.7.0"
433
+ }
434
+ };
435
+ var solidPackagePlan = {
436
+ scripts: {
437
+ dev: "vite",
438
+ build: "tsc -b && vite build",
439
+ preview: "vite preview"
440
+ },
441
+ dependencies: {
442
+ "@decantr/css": "^1.0.4",
443
+ "@solidjs/router": "^0.15.0",
444
+ lucide: "^0.468.0",
445
+ "solid-js": "^1.9.0"
446
+ },
447
+ devDependencies: {
448
+ typescript: "^5.7.0",
449
+ vite: "^6.0.0",
450
+ "vite-plugin-solid": "^2.11.0"
451
+ }
452
+ };
453
+ function writeReactViteProject(projectDir, title, routingMode) {
454
+ const srcDir = join3(projectDir, "src");
455
+ const routerImport = routingMode === "hash" ? "HashRouter" : "BrowserRouter";
456
+ writeJson(join3(projectDir, "package.json"), packagePlanPackageJson(projectDir, "decantr-app", reactVitePackagePlan));
457
+ writeFileSync3(
458
+ join3(projectDir, "vite.config.ts"),
459
+ `import { defineConfig } from 'vite';
460
+ import react from '@vitejs/plugin-react';
461
+
462
+ export default defineConfig({
463
+ plugins: [react()],
464
+ });
465
+ `
466
+ );
467
+ writeViteTsConfig(projectDir, { jsx: "react-jsx" });
468
+ writeJson(join3(projectDir, "tsconfig.app.json"), JSON.parse(readFileSync3(join3(projectDir, "tsconfig.json"), "utf-8")));
469
+ writeIndexHtml(projectDir, title, "/src/main.tsx");
470
+ mkdirp(srcDir);
471
+ writeFileSync3(
472
+ join3(srcDir, "main.tsx"),
473
+ `import { StrictMode } from 'react';
378
474
  import { createRoot } from 'react-dom/client';
379
475
  import { ${routerImport} } from 'react-router-dom';
380
476
  import { App } from './App';
@@ -389,9 +485,11 @@ createRoot(document.getElementById('root')!).render(
389
485
  </${routerImport}>
390
486
  </StrictMode>,
391
487
  );
392
- `;
393
- writeFileSync3(join3(srcDir, "main.tsx"), mainTsx);
394
- const appTsx = `import { css } from '@decantr/css';
488
+ `
489
+ );
490
+ writeFileSync3(
491
+ join3(srcDir, "App.tsx"),
492
+ `import { css } from '@decantr/css';
395
493
  import { Routes, Route } from 'react-router-dom';
396
494
 
397
495
  function WelcomePage() {
@@ -403,9 +501,7 @@ function WelcomePage() {
403
501
  <div className={css('_flex _col _aic _gap4 _textc') + ' d-surface'} data-elevation="raised">
404
502
  <p className="d-label" data-anchor>Decantr starter</p>
405
503
  <h1 className={css('_heading2')}>${title}</h1>
406
- <p className={css('_textsm _fgmuted _mw[32rem]')}>
407
- Scaffolded with Decantr. Read .decantr/context/scaffold-pack.md first, then use DECANTR.md as a lookup reference.
408
- </p>
504
+ <p className={css('_textsm _fgmuted _mw[32rem]')}>Scaffolded with Decantr. Read .decantr/context/scaffold-pack.md first, then use DECANTR.md as a lookup reference.</p>
409
505
  <div className={css('_flex _gap3 _wrap _jcc')}>
410
506
  <span className="d-annotation" data-status="info">Runtime: @decantr/css</span>
411
507
  <span className="d-annotation" data-status="success">Routing: ${routingMode}</span>
@@ -424,102 +520,42 @@ export function App() {
424
520
  </Routes>
425
521
  );
426
522
  }
427
- `;
428
- writeFileSync3(join3(srcDir, "App.tsx"), appTsx);
429
- writeFileSync3(join3(srcDir, "vite-env.d.ts"), '/// <reference types="vite/client" />\n');
430
- mkdirSync3(join3(srcDir, "styles"), { recursive: true });
431
- }
432
- };
433
- var nextAppAdapter = {
434
- id: "next-app",
435
- label: "Next.js App Router starter",
436
- target: "nextjs",
437
- packAdapter: "nextjs",
438
- capabilities: {
439
- bootstrap: true,
440
- realize: true,
441
- attach: true,
442
- styling: true,
443
- verify: true
444
- },
445
- attach: {
446
- routeRoots: ["app", "pages"],
447
- layoutFiles: ["app/layout.tsx", "pages/_app.tsx"],
448
- componentRoots: ["components", "src/components", "app"]
449
- },
450
- verify: {
451
- devCommand: "npm run dev",
452
- buildCommand: "npm run build",
453
- distDir: ".next"
454
- },
455
- writeProjectFiles(projectDir, title) {
456
- const appDir = join3(projectDir, "app");
457
- const stylesDir = join3(projectDir, "src", "styles");
458
- const packageJson = {
459
- name: basename(projectDir) || "decantr-next-app",
460
- private: true,
461
- version: "0.0.0",
462
- type: "module",
463
- scripts: {
464
- dev: "next dev",
465
- build: "next build",
466
- start: "next start"
467
- },
468
- dependencies: {
469
- "@decantr/css": "^1.0.4",
470
- "lucide-react": "^0.468.0",
471
- next: "^16.0.0",
472
- react: "^19.0.0",
473
- "react-dom": "^19.0.0"
474
- },
475
- devDependencies: {
476
- "@types/node": "^20.0.0",
477
- "@types/react": "^19.0.0",
478
- "@types/react-dom": "^19.0.0",
479
- typescript: "^5.7.0"
480
- }
481
- };
482
- writeFileSync3(join3(projectDir, "package.json"), JSON.stringify(packageJson, null, 2) + "\n");
483
- writeFileSync3(
484
- join3(projectDir, "next.config.ts"),
485
- 'import type { NextConfig } from "next";\n\nconst nextConfig: NextConfig = {};\n\nexport default nextConfig;\n'
486
- );
487
- writeFileSync3(
488
- join3(projectDir, "next-env.d.ts"),
489
- '/// <reference types="next" />\n/// <reference types="next/image-types/global" />\n\n// This file is generated by Next.js.\n'
490
- );
491
- writeFileSync3(
492
- join3(projectDir, "tsconfig.json"),
493
- JSON.stringify(
494
- {
495
- compilerOptions: {
496
- target: "ES2017",
497
- lib: ["dom", "dom.iterable", "esnext"],
498
- allowJs: true,
499
- skipLibCheck: true,
500
- strict: true,
501
- noEmit: true,
502
- esModuleInterop: true,
503
- module: "esnext",
504
- moduleResolution: "bundler",
505
- resolveJsonModule: true,
506
- isolatedModules: true,
507
- jsx: "preserve",
508
- incremental: true,
509
- plugins: [{ name: "next" }]
510
- },
511
- include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
512
- exclude: ["node_modules"]
513
- },
514
- null,
515
- 2
516
- ) + "\n"
517
- );
518
- mkdirSync3(appDir, { recursive: true });
519
- mkdirSync3(stylesDir, { recursive: true });
520
- writeFileSync3(
521
- join3(appDir, "layout.tsx"),
522
- `import type { Metadata } from 'next';
523
+ `
524
+ );
525
+ writeFileSync3(join3(srcDir, "vite-env.d.ts"), '/// <reference types="vite/client" />\n');
526
+ ensureStyleDir(projectDir);
527
+ }
528
+ function writeNextProject(projectDir, title) {
529
+ const appDir = join3(projectDir, "app");
530
+ const stylesDir = join3(projectDir, "src", "styles");
531
+ writeJson(join3(projectDir, "package.json"), packagePlanPackageJson(projectDir, "decantr-next-app", nextPackagePlan));
532
+ writeFileSync3(join3(projectDir, "next.config.ts"), 'import type { NextConfig } from "next";\n\nconst nextConfig: NextConfig = {};\n\nexport default nextConfig;\n');
533
+ writeFileSync3(join3(projectDir, "next-env.d.ts"), '/// <reference types="next" />\n/// <reference types="next/image-types/global" />\n\n// This file is generated by Next.js.\n');
534
+ writeJson(join3(projectDir, "tsconfig.json"), {
535
+ compilerOptions: {
536
+ target: "ES2017",
537
+ lib: ["dom", "dom.iterable", "esnext"],
538
+ allowJs: true,
539
+ skipLibCheck: true,
540
+ strict: true,
541
+ noEmit: true,
542
+ esModuleInterop: true,
543
+ module: "esnext",
544
+ moduleResolution: "bundler",
545
+ resolveJsonModule: true,
546
+ isolatedModules: true,
547
+ jsx: "preserve",
548
+ incremental: true,
549
+ plugins: [{ name: "next" }]
550
+ },
551
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
552
+ exclude: ["node_modules"]
553
+ });
554
+ mkdirp(appDir);
555
+ mkdirp(stylesDir);
556
+ writeFileSync3(
557
+ join3(appDir, "layout.tsx"),
558
+ `import type { Metadata } from 'next';
523
559
  import '../src/styles/global.css';
524
560
  import '../src/styles/tokens.css';
525
561
  import '../src/styles/treatments.css';
@@ -537,10 +573,10 @@ export default function RootLayout({ children }: Readonly<{ children: React.Reac
537
573
  );
538
574
  }
539
575
  `
540
- );
541
- writeFileSync3(
542
- join3(appDir, "page.tsx"),
543
- `import { css } from '@decantr/css';
576
+ );
577
+ writeFileSync3(
578
+ join3(appDir, "page.tsx"),
579
+ `import { css } from '@decantr/css';
544
580
 
545
581
  export default function HomePage() {
546
582
  return (
@@ -557,54 +593,486 @@ export default function HomePage() {
557
593
  );
558
594
  }
559
595
  `
560
- );
561
- }
596
+ );
597
+ }
598
+ function writeVanillaProject(projectDir, title, routingMode) {
599
+ const srcDir = join3(projectDir, "src");
600
+ writeJson(join3(projectDir, "package.json"), packagePlanPackageJson(projectDir, "decantr-vanilla-app", vanillaPackagePlan));
601
+ writeIndexHtml(projectDir, title, "/src/main.js");
602
+ mkdirp(srcDir);
603
+ writeFileSync3(
604
+ join3(srcDir, "main.js"),
605
+ `import { css } from '@decantr/css';
606
+ import './styles/global.css';
607
+ import './styles/tokens.css';
608
+ import './styles/treatments.css';
609
+
610
+ const root = document.getElementById('root');
611
+ if (!root) throw new Error('Missing #root element.');
612
+
613
+ root.innerHTML = \`
614
+ <a href="#main-content" class="skip-link">Skip to content</a>
615
+ <main id="main-content" class="\${css('_minh[100vh] _flex _col _aic _jcc _p6 _gap4')}">
616
+ <section class="\${css('_wfull _mw[42rem]')} d-section" data-density="comfortable">
617
+ <div class="\${css('_flex _col _aic _gap4 _textc')} d-surface" data-elevation="raised">
618
+ <p class="d-label" data-anchor>Decantr starter</p>
619
+ <h1 class="\${css('_heading2')}">${title}</h1>
620
+ <p class="\${css('_textsm _fgmuted _mw[32rem]')}">Scaffolded with Decantr. Read .decantr/context/scaffold-pack.md first, then use DECANTR.md as a lookup reference.</p>
621
+ <div class="\${css('_flex _gap3 _wrap _jcc')}">
622
+ <span class="d-annotation" data-status="info">Runtime: vanilla Vite</span>
623
+ <span class="d-annotation" data-status="success">Routing: ${routingMode}</span>
624
+ </div>
625
+ </div>
626
+ </section>
627
+ </main>
628
+ \`;
629
+ `
630
+ );
631
+ ensureStyleDir(projectDir);
632
+ }
633
+ function writeVueProject(projectDir, title, routingMode) {
634
+ const srcDir = join3(projectDir, "src");
635
+ const historyFactory = routingMode === "hash" ? "createWebHashHistory" : "createWebHistory";
636
+ writeJson(join3(projectDir, "package.json"), packagePlanPackageJson(projectDir, "decantr-vue-app", vuePackagePlan));
637
+ writeFileSync3(join3(projectDir, "vite.config.ts"), `import { defineConfig } from 'vite';
638
+ import vue from '@vitejs/plugin-vue';
639
+
640
+ export default defineConfig({ plugins: [vue()] });
641
+ `);
642
+ writeViteTsConfig(projectDir);
643
+ writeFileSync3(join3(projectDir, "env.d.ts"), '/// <reference types="vite/client" />\n\ndeclare module "*.vue";\n');
644
+ writeIndexHtml(projectDir, title, "/src/main.ts");
645
+ mkdirp(srcDir);
646
+ writeFileSync3(
647
+ join3(srcDir, "main.ts"),
648
+ `import { createApp } from 'vue';
649
+ import { createRouter, ${historyFactory} } from 'vue-router';
650
+ import App from './App.vue';
651
+ import './styles/global.css';
652
+ import './styles/tokens.css';
653
+ import './styles/treatments.css';
654
+
655
+ const router = createRouter({
656
+ history: ${historyFactory}(),
657
+ routes: [{ path: '/', component: App }],
658
+ });
659
+
660
+ createApp(App).use(router).mount('#root');
661
+ `
662
+ );
663
+ writeFileSync3(
664
+ join3(srcDir, "App.vue"),
665
+ `<script setup lang="ts">
666
+ import { css } from '@decantr/css';
667
+ </script>
668
+
669
+ <template>
670
+ <a href="#main-content" class="skip-link">Skip to content</a>
671
+ <main id="main-content" :class="css('_minh[100vh] _flex _col _aic _jcc _p6 _gap4')">
672
+ <section :class="css('_wfull _mw[42rem]') + ' d-section'" data-density="comfortable">
673
+ <div :class="css('_flex _col _aic _gap4 _textc') + ' d-surface'" data-elevation="raised">
674
+ <p class="d-label" data-anchor>Decantr starter</p>
675
+ <h1 :class="css('_heading2')">${title}</h1>
676
+ <p :class="css('_textsm _fgmuted _mw[32rem]')">Scaffolded with Decantr. Read .decantr/context/scaffold-pack.md first, then use DECANTR.md as a lookup reference.</p>
677
+ <div :class="css('_flex _gap3 _wrap _jcc')">
678
+ <span class="d-annotation" data-status="info">Runtime: Vue + Vite</span>
679
+ <span class="d-annotation" data-status="success">Routing: ${routingMode}</span>
680
+ </div>
681
+ </div>
682
+ </section>
683
+ </main>
684
+ </template>
685
+ `
686
+ );
687
+ ensureStyleDir(projectDir);
688
+ }
689
+ function writeSvelteProject(projectDir, title) {
690
+ const routesDir = join3(projectDir, "src", "routes");
691
+ writeJson(join3(projectDir, "package.json"), packagePlanPackageJson(projectDir, "decantr-svelte-app", sveltePackagePlan));
692
+ writeFileSync3(join3(projectDir, "svelte.config.js"), `import adapter from '@sveltejs/adapter-auto';
693
+ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
694
+
695
+ export default { preprocess: vitePreprocess(), kit: { adapter: adapter() } };
696
+ `);
697
+ writeFileSync3(join3(projectDir, "vite.config.ts"), `import { sveltekit } from '@sveltejs/kit/vite';
698
+ import { defineConfig } from 'vite';
699
+
700
+ export default defineConfig({ plugins: [sveltekit()] });
701
+ `);
702
+ writeViteTsConfig(projectDir);
703
+ mkdirp(routesDir);
704
+ writeFileSync3(join3(projectDir, "src", "app.html"), '<!doctype html>\n<html lang="en">\n <head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width, initial-scale=1" />\n %sveltekit.head%\n </head>\n <body data-sveltekit-preload-data="hover">\n <div style="display: contents">%sveltekit.body%</div>\n </body>\n</html>\n');
705
+ writeFileSync3(join3(routesDir, "+layout.svelte"), `<script lang="ts">
706
+ import '../styles/global.css';
707
+ import '../styles/tokens.css';
708
+ import '../styles/treatments.css';
709
+ let { children } = $props();
710
+ </script>
711
+
712
+ {@render children()}
713
+ `);
714
+ writeFileSync3(
715
+ join3(routesDir, "+page.svelte"),
716
+ `<script lang="ts">
717
+ import { css } from '@decantr/css';
718
+ </script>
719
+
720
+ <svelte:head>
721
+ <title>${title}</title>
722
+ </svelte:head>
723
+
724
+ <a href="#main-content" class="skip-link">Skip to content</a>
725
+ <main id="main-content" class={css('_minh[100vh] _flex _col _aic _jcc _p6 _gap4')}>
726
+ <section class={css('_wfull _mw[42rem]') + ' d-section'} data-density="comfortable">
727
+ <div class={css('_flex _col _aic _gap4 _textc') + ' d-surface'} data-elevation="raised">
728
+ <p class="d-label" data-anchor>Decantr starter</p>
729
+ <h1 class={css('_heading2')}>${title}</h1>
730
+ <p class={css('_textsm _fgmuted _mw[32rem]')}>Scaffolded with Decantr. Read .decantr/context/scaffold-pack.md first, then use DECANTR.md as a lookup reference.</p>
731
+ <span class="d-annotation" data-status="info">Runtime: SvelteKit</span>
732
+ </div>
733
+ </section>
734
+ </main>
735
+ `
736
+ );
737
+ ensureStyleDir(projectDir);
738
+ }
739
+ function writeAngularProject(projectDir, title) {
740
+ const appDir = join3(projectDir, "src", "app");
741
+ writeJson(join3(projectDir, "package.json"), packagePlanPackageJson(projectDir, "decantr-angular-app", angularPackagePlan));
742
+ writeJson(join3(projectDir, "angular.json"), {
743
+ version: 1,
744
+ projects: {
745
+ app: {
746
+ projectType: "application",
747
+ root: "",
748
+ sourceRoot: "src",
749
+ architect: {
750
+ build: {
751
+ builder: "@angular-devkit/build-angular:application",
752
+ options: {
753
+ outputPath: "dist",
754
+ index: "src/index.html",
755
+ browser: "src/main.ts",
756
+ tsConfig: "tsconfig.app.json",
757
+ styles: ["src/styles.css", "src/styles/tokens.css", "src/styles/treatments.css"]
758
+ }
759
+ },
760
+ serve: {
761
+ builder: "@angular-devkit/build-angular:dev-server",
762
+ options: { buildTarget: "app:build" }
763
+ }
764
+ }
765
+ }
766
+ }
767
+ });
768
+ writeJson(join3(projectDir, "tsconfig.json"), {
769
+ compilerOptions: {
770
+ target: "ES2022",
771
+ module: "ES2022",
772
+ moduleResolution: "bundler",
773
+ strict: true,
774
+ skipLibCheck: true,
775
+ experimentalDecorators: true,
776
+ useDefineForClassFields: false,
777
+ lib: ["ES2022", "DOM"]
778
+ }
779
+ });
780
+ writeJson(join3(projectDir, "tsconfig.app.json"), { extends: "./tsconfig.json", files: ["src/main.ts"], include: ["src/**/*.d.ts"] });
781
+ mkdirp(appDir);
782
+ writeFileSync3(join3(projectDir, "src", "index.html"), `<!doctype html>
783
+ <html lang="en">
784
+ <head><meta charset="utf-8"><title>${title}</title><meta name="viewport" content="width=device-width, initial-scale=1"></head>
785
+ <body><app-root></app-root></body>
786
+ </html>
787
+ `);
788
+ writeFileSync3(join3(projectDir, "src", "main.ts"), `import { bootstrapApplication } from '@angular/platform-browser';
789
+ import { provideRouter } from '@angular/router';
790
+ import { AppComponent } from './app/app.component';
791
+ import { routes } from './app/app.routes';
792
+
793
+ bootstrapApplication(AppComponent, { providers: [provideRouter(routes)] }).catch((err) => console.error(err));
794
+ `);
795
+ writeFileSync3(join3(projectDir, "src", "styles.css"), '@import "./styles/global.css";\n');
796
+ writeFileSync3(join3(appDir, "app.routes.ts"), `import type { Routes } from '@angular/router';
797
+ import { AppComponent } from './app.component';
798
+
799
+ export const routes: Routes = [{ path: '', component: AppComponent }];
800
+ `);
801
+ writeFileSync3(
802
+ join3(appDir, "app.component.ts"),
803
+ `import { Component } from '@angular/core';
804
+
805
+ @Component({
806
+ selector: 'app-root',
807
+ standalone: true,
808
+ template: \`
809
+ <a href="#main-content" class="skip-link">Skip to content</a>
810
+ <main id="main-content" class="_minh[100vh] _flex _col _aic _jcc _p6 _gap4">
811
+ <section class="_wfull _mw[42rem] d-section" data-density="comfortable">
812
+ <div class="_flex _col _aic _gap4 _textc d-surface" data-elevation="raised">
813
+ <p class="d-label" data-anchor>Decantr starter</p>
814
+ <h1 class="_heading2">${title}</h1>
815
+ <p class="_textsm _fgmuted _mw[32rem]">Scaffolded with Decantr. Read .decantr/context/scaffold-pack.md first, then use DECANTR.md as a lookup reference.</p>
816
+ <span class="d-annotation" data-status="info">Runtime: Angular</span>
817
+ </div>
818
+ </section>
819
+ </main>
820
+ \`,
821
+ })
822
+ export class AppComponent {}
823
+ `
824
+ );
825
+ ensureStyleDir(projectDir);
826
+ }
827
+ function writeSolidProject(projectDir, title, routingMode) {
828
+ const srcDir = join3(projectDir, "src");
829
+ const routerImport = routingMode === "hash" ? "HashRouter" : "Router";
830
+ writeJson(join3(projectDir, "package.json"), packagePlanPackageJson(projectDir, "decantr-solid-app", solidPackagePlan));
831
+ writeFileSync3(join3(projectDir, "vite.config.ts"), `import { defineConfig } from 'vite';
832
+ import solid from 'vite-plugin-solid';
833
+
834
+ export default defineConfig({ plugins: [solid()] });
835
+ `);
836
+ writeViteTsConfig(projectDir, { jsx: "preserve" });
837
+ writeJson(join3(projectDir, "tsconfig.app.json"), JSON.parse(readFileSync3(join3(projectDir, "tsconfig.json"), "utf-8")));
838
+ writeIndexHtml(projectDir, title, "/src/main.tsx");
839
+ mkdirp(srcDir);
840
+ writeFileSync3(
841
+ join3(srcDir, "main.tsx"),
842
+ `import { render } from 'solid-js/web';
843
+ import { ${routerImport} } from '@solidjs/router';
844
+ import { App } from './App';
845
+ import './styles/global.css';
846
+ import './styles/tokens.css';
847
+ import './styles/treatments.css';
848
+
849
+ render(() => (
850
+ <${routerImport}>
851
+ <App />
852
+ </${routerImport}>
853
+ ), document.getElementById('root')!);
854
+ `
855
+ );
856
+ writeFileSync3(
857
+ join3(srcDir, "App.tsx"),
858
+ `import { css } from '@decantr/css';
859
+
860
+ export function App() {
861
+ return (
862
+ <>
863
+ <a href="#main-content" class="skip-link">Skip to content</a>
864
+ <main id="main-content" class={css('_minh[100vh] _flex _col _aic _jcc _p6 _gap4')}>
865
+ <section class={css('_wfull _mw[42rem]') + ' d-section'} data-density="comfortable">
866
+ <div class={css('_flex _col _aic _gap4 _textc') + ' d-surface'} data-elevation="raised">
867
+ <p class="d-label" data-anchor>Decantr starter</p>
868
+ <h1 class={css('_heading2')}>${title}</h1>
869
+ <p class={css('_textsm _fgmuted _mw[32rem]')}>Scaffolded with Decantr. Read .decantr/context/scaffold-pack.md first, then use DECANTR.md as a lookup reference.</p>
870
+ <div class={css('_flex _gap3 _wrap _jcc')}>
871
+ <span class="d-annotation" data-status="info">Runtime: Solid + Vite</span>
872
+ <span class="d-annotation" data-status="success">Routing: ${routingMode}</span>
873
+ </div>
874
+ </div>
875
+ </section>
876
+ </main>
877
+ </>
878
+ );
879
+ }
880
+ `
881
+ );
882
+ ensureStyleDir(projectDir);
883
+ }
884
+ var certifiedCapabilities = {
885
+ bootstrap: true,
886
+ realize: true,
887
+ attach: true,
888
+ styling: true,
889
+ verify: true
562
890
  };
563
- var genericWebAdapter = {
564
- id: "generic-web",
565
- label: "Generic web contract adapter",
566
- target: "generic",
567
- packAdapter: "generic-web",
568
- capabilities: {
569
- bootstrap: false,
570
- realize: false,
571
- attach: true,
572
- styling: true,
573
- verify: true
891
+ var DECANTR_ADAPTERS = {
892
+ "react-vite": {
893
+ id: "react-vite",
894
+ label: "React + Vite starter",
895
+ target: "react",
896
+ aliases: ["react"],
897
+ status: "certified",
898
+ packAdapter: "react-vite",
899
+ routing: "history",
900
+ capabilities: certifiedCapabilities,
901
+ attach: {
902
+ routeRoots: ["src/App.tsx", "src/routes", "src/pages"],
903
+ layoutFiles: ["src/App.tsx", "src/main.tsx"],
904
+ componentRoots: ["src/components", "src/pages", "src/routes"]
905
+ },
906
+ verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: "dist" },
907
+ packagePlan: reactVitePackagePlan,
908
+ docs: { summary: "React + Vite runnable starter.", notes: ["Uses React Router and @decantr/css."] },
909
+ writeProjectFiles: writeReactViteProject
910
+ },
911
+ "next-app": {
912
+ id: "next-app",
913
+ label: "Next.js App Router starter",
914
+ target: "nextjs",
915
+ aliases: ["next", "nextjs"],
916
+ status: "certified",
917
+ packAdapter: "nextjs",
918
+ routing: "pathname",
919
+ capabilities: certifiedCapabilities,
920
+ attach: {
921
+ routeRoots: ["app", "pages"],
922
+ layoutFiles: ["app/layout.tsx", "pages/_app.tsx"],
923
+ componentRoots: ["components", "src/components", "app"]
924
+ },
925
+ verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: ".next" },
926
+ packagePlan: nextPackagePlan,
927
+ docs: { summary: "Next.js App Router runnable starter.", notes: ["Uses App Router for greenfield bootstraps."] },
928
+ writeProjectFiles: writeNextProject
929
+ },
930
+ "vanilla-vite": {
931
+ id: "vanilla-vite",
932
+ label: "Vanilla HTML/CSS/JS + Vite starter",
933
+ target: "html",
934
+ aliases: ["html", "vanilla", "javascript", "js"],
935
+ status: "certified",
936
+ packAdapter: "vanilla-vite",
937
+ routing: "history",
938
+ capabilities: certifiedCapabilities,
939
+ attach: {
940
+ routeRoots: ["index.html", "src/main.js", "src/routes"],
941
+ layoutFiles: ["index.html", "src/main.js"],
942
+ componentRoots: ["src/components", "src"]
943
+ },
944
+ verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: "dist" },
945
+ packagePlan: vanillaPackagePlan,
946
+ docs: { summary: "Plain web runnable starter.", notes: ["Uses DOM APIs and @decantr/css without a UI framework."] },
947
+ writeProjectFiles: writeVanillaProject
948
+ },
949
+ "vue-vite": {
950
+ id: "vue-vite",
951
+ label: "Vue + Vite starter",
952
+ target: "vue",
953
+ aliases: ["vue", "vue3"],
954
+ status: "certified",
955
+ packAdapter: "vue-vite",
956
+ routing: "history",
957
+ capabilities: certifiedCapabilities,
958
+ attach: {
959
+ routeRoots: ["src/main.ts", "src/router.ts", "src/views"],
960
+ layoutFiles: ["src/App.vue", "src/main.ts"],
961
+ componentRoots: ["src/components", "src/views"]
962
+ },
963
+ verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: "dist" },
964
+ packagePlan: vuePackagePlan,
965
+ docs: { summary: "Vue 3 + Vite runnable starter.", notes: ["Uses Vue Router and @decantr/css."] },
966
+ writeProjectFiles: writeVueProject
967
+ },
968
+ sveltekit: {
969
+ id: "sveltekit",
970
+ label: "SvelteKit starter",
971
+ target: "svelte",
972
+ aliases: ["svelte", "sveltekit"],
973
+ status: "certified",
974
+ packAdapter: "sveltekit",
975
+ routing: "framework-native",
976
+ capabilities: certifiedCapabilities,
977
+ attach: {
978
+ routeRoots: ["src/routes"],
979
+ layoutFiles: ["src/routes/+layout.svelte", "src/app.html"],
980
+ componentRoots: ["src/lib", "src/routes"]
981
+ },
982
+ verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: ".svelte-kit" },
983
+ packagePlan: sveltePackagePlan,
984
+ docs: { summary: "SvelteKit runnable starter.", notes: ["Uses SvelteKit file routing and @decantr/css."] },
985
+ writeProjectFiles: writeSvelteProject
986
+ },
987
+ angular: {
988
+ id: "angular",
989
+ label: "Angular standalone starter",
990
+ target: "angular",
991
+ aliases: ["angular", "ng"],
992
+ status: "certified",
993
+ packAdapter: "angular",
994
+ routing: "framework-native",
995
+ capabilities: certifiedCapabilities,
996
+ attach: {
997
+ routeRoots: ["src/app/app.routes.ts"],
998
+ layoutFiles: ["src/app/app.component.ts", "src/main.ts"],
999
+ componentRoots: ["src/app"]
1000
+ },
1001
+ verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: "dist" },
1002
+ packagePlan: angularPackagePlan,
1003
+ docs: { summary: "Angular standalone runnable starter.", notes: ["Uses Angular Router and global Decantr CSS files."] },
1004
+ writeProjectFiles: writeAngularProject
574
1005
  },
575
- attach: {
576
- routeRoots: ["src", "app", "pages"],
577
- layoutFiles: ["index.html", "src/main.ts", "src/main.tsx"],
578
- componentRoots: ["src/components", "components"]
1006
+ "solid-vite": {
1007
+ id: "solid-vite",
1008
+ label: "Solid + Vite starter",
1009
+ target: "solid",
1010
+ aliases: ["solid", "solidjs"],
1011
+ status: "certified",
1012
+ packAdapter: "solid-vite",
1013
+ routing: "history",
1014
+ capabilities: certifiedCapabilities,
1015
+ attach: {
1016
+ routeRoots: ["src/App.tsx", "src/routes", "src/pages"],
1017
+ layoutFiles: ["src/App.tsx", "src/main.tsx"],
1018
+ componentRoots: ["src/components", "src/pages", "src/routes"]
1019
+ },
1020
+ verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: "dist" },
1021
+ packagePlan: solidPackagePlan,
1022
+ docs: { summary: "Solid + Vite runnable starter.", notes: ["Uses @solidjs/router and @decantr/css."] },
1023
+ writeProjectFiles: writeSolidProject
579
1024
  },
580
- verify: {
581
- devCommand: "npm run dev",
582
- buildCommand: "npm run build",
583
- distDir: "dist"
1025
+ "generic-web": {
1026
+ id: "generic-web",
1027
+ label: "Generic web contract adapter",
1028
+ target: "generic",
1029
+ aliases: [],
1030
+ status: "contract-only",
1031
+ packAdapter: "generic-web",
1032
+ routing: "history",
1033
+ capabilities: { bootstrap: false, realize: false, attach: true, styling: true, verify: true },
1034
+ attach: {
1035
+ routeRoots: ["src", "app", "pages"],
1036
+ layoutFiles: ["index.html", "src/main.ts", "src/main.tsx"],
1037
+ componentRoots: ["src/components", "components"]
1038
+ },
1039
+ verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: "dist" },
1040
+ docs: { summary: "Contract-only fallback for unsupported web targets.", notes: ["Does not write framework runtime files."] }
584
1041
  }
585
1042
  };
586
- var DECANTR_ADAPTERS = {
587
- "react-vite": reactViteBootstrapAdapter,
588
- "next-app": nextAppAdapter,
589
- "generic-web": genericWebAdapter
590
- };
1043
+ var TARGET_ALIASES = /* @__PURE__ */ new Map();
1044
+ for (const adapter of Object.values(DECANTR_ADAPTERS)) {
1045
+ TARGET_ALIASES.set(adapter.target, adapter.id);
1046
+ TARGET_ALIASES.set(adapter.id, adapter.id);
1047
+ for (const alias of adapter.aliases) TARGET_ALIASES.set(alias, adapter.id);
1048
+ }
1049
+ function normalizeTarget(target) {
1050
+ return (target || "react").toLowerCase();
1051
+ }
1052
+ function platformTypeForTarget(target) {
1053
+ return target === "nextjs" || target === "next" || target === "sveltekit" ? "ssr" : "spa";
1054
+ }
1055
+ function adapterIdForTarget(target, packAdapter) {
1056
+ return TARGET_ALIASES.get(target) ?? (DECANTR_ADAPTERS[packAdapter] ? packAdapter : "generic-web");
1057
+ }
591
1058
  function resolveBootstrapTarget(target) {
592
- const normalizedTarget = (target || "react").toLowerCase();
593
- const platformType = normalizedTarget === "nextjs" ? "ssr" : "spa";
594
- const packAdapter = resolvePackAdapter(normalizedTarget, platformType);
595
- const adapterId = normalizedTarget === "nextjs" ? "next-app" : DECANTR_ADAPTERS[packAdapter] ? packAdapter : "generic-web";
1059
+ const normalizedTarget = normalizeTarget(target);
1060
+ const adapterIdFromAlias = TARGET_ALIASES.get(normalizedTarget);
1061
+ const adapterTarget = adapterIdFromAlias ? DECANTR_ADAPTERS[adapterIdFromAlias].target : normalizedTarget;
1062
+ const platformType = platformTypeForTarget(adapterTarget);
1063
+ const packAdapter = resolvePackAdapter(adapterTarget, platformType);
1064
+ const adapterId = adapterIdForTarget(adapterTarget, packAdapter);
1065
+ const adapter = DECANTR_ADAPTERS[adapterId];
596
1066
  return {
597
- target: normalizedTarget,
1067
+ target: adapterTarget,
598
1068
  platformType,
599
1069
  packAdapter,
600
1070
  adapterId,
601
- bootstrapAdapterId: DECANTR_ADAPTERS[adapterId]?.capabilities.bootstrap && DECANTR_ADAPTERS[adapterId]?.writeProjectFiles ? adapterId : null
1071
+ bootstrapAdapterId: adapter?.capabilities.bootstrap && adapter?.writeProjectFiles ? adapterId : null
602
1072
  };
603
1073
  }
604
1074
  function getBootstrapAdapter(resolution) {
605
- if (!resolution.bootstrapAdapterId) {
606
- return null;
607
- }
1075
+ if (!resolution.bootstrapAdapterId) return null;
608
1076
  const adapter = DECANTR_ADAPTERS[resolution.bootstrapAdapterId];
609
1077
  return adapter?.writeProjectFiles ? adapter : null;
610
1078
  }
@@ -612,9 +1080,7 @@ function detectRoutingMode(projectDir) {
612
1080
  try {
613
1081
  const essence = JSON.parse(readFileSync3(join3(projectDir, "decantr.essence.json"), "utf-8"));
614
1082
  const routing = essence.meta?.platform?.routing;
615
- if (routing === "history" || routing === "pathname") {
616
- return routing;
617
- }
1083
+ if (routing === "history" || routing === "pathname") return routing;
618
1084
  return "hash";
619
1085
  } catch {
620
1086
  return "hash";
@@ -1964,6 +2430,9 @@ function detectProject(projectRoot = process.cwd()) {
1964
2430
  } else if (deps["@angular/core"]) {
1965
2431
  result.framework = "angular";
1966
2432
  result.version = deps["@angular/core"].replace(/^\^|~/, "");
2433
+ } else if (deps["solid-js"]) {
2434
+ result.framework = "solid";
2435
+ result.version = deps["solid-js"].replace(/^\^|~/, "");
1967
2436
  } else if (deps.vue) {
1968
2437
  result.framework = "vue";
1969
2438
  result.version = deps.vue.replace(/^\^|~/, "");
@@ -2660,6 +3129,22 @@ function generateCSSVars(tokens) {
2660
3129
  lines.push("}", "");
2661
3130
  return lines.join("\n");
2662
3131
  }
3132
+ function generateFigmaTokens(tokens) {
3133
+ const out = {};
3134
+ for (const [key, value] of tokens) {
3135
+ const name = key.replace(/^--/, "").replace(/-/g, ".");
3136
+ const type = /color|bg|surface|text|border|primary|secondary|accent|error|warning|success|info/.test(
3137
+ key
3138
+ ) ? "color" : /radius/.test(key) ? "borderRadius" : /shadow/.test(key) ? "shadow" : /gap|space|spacing/.test(key) ? "dimension" : "string";
3139
+ out[name] = {
3140
+ $type: type,
3141
+ $value: value,
3142
+ $description: `Exported from ${key} by Decantr.`
3143
+ };
3144
+ }
3145
+ return `${JSON.stringify(out, null, 2)}
3146
+ `;
3147
+ }
2663
3148
  async function cmdExport(target, projectRoot, options = {}) {
2664
3149
  const essencePath = join14(projectRoot, "decantr.essence.json");
2665
3150
  const tokensPath = join14(projectRoot, "src", "styles", "tokens.css");
@@ -2710,6 +3195,14 @@ async function cmdExport(target, projectRoot, options = {}) {
2710
3195
  console.log(` ${DIM3}File:${RESET3} ${out}`);
2711
3196
  break;
2712
3197
  }
3198
+ case "figma-tokens": {
3199
+ const out = options.output ?? join14(projectRoot, ".decantr", "design", "figma-tokens.json");
3200
+ ensureDir(out);
3201
+ writeFileSync8(out, generateFigmaTokens(tokens), "utf-8");
3202
+ console.log(`${GREEN3}Exported Figma/Tokens Studio tokens:${RESET3}`);
3203
+ console.log(` ${DIM3}File:${RESET3} ${out}`);
3204
+ break;
3205
+ }
2713
3206
  }
2714
3207
  }
2715
3208
  function ensureDir(filePath) {
@@ -4632,6 +5125,7 @@ async function runInteractivePrompts(detected, archetypes, blueprints, themes, w
4632
5125
  { value: "svelte", label: "svelte", description: "Svelte / SvelteKit" },
4633
5126
  { value: "astro", label: "astro", description: "Astro" },
4634
5127
  { value: "angular", label: "angular", description: "Angular" },
5128
+ { value: "solid", label: "solid", description: "SolidJS" },
4635
5129
  { value: "html", label: "html", description: "Plain HTML/CSS/JS" }
4636
5130
  ];
4637
5131
  let defaultFrameworkIdx = frameworkOptions.findIndex(
@@ -7312,9 +7806,12 @@ ${BOLD7}Usage:${RESET14}
7312
7806
  decantr registry critique-file <file> [--namespace <namespace>] [--json] [--essence <path>] [--treatments <path>]
7313
7807
  decantr registry audit-project [--namespace <namespace>] [--json] [--essence <path>] [--dist <path>] [--sources <dir>]
7314
7808
  decantr health [--format text|json|markdown] [--ci] [--fail-on error|warn|none]
7315
- decantr health init-ci [--force] [--project <path>] [--fail-on <error|warn|none>] [--cli-version <version|latest>]
7809
+ decantr health --evidence [--browser] [--design-tokens <path>]
7810
+ decantr health init-ci [--force] [--project <path>] [--workspace] [--fail-on <error|warn|none>] [--cli-version <version|latest>]
7811
+ decantr workspace list [--json]
7812
+ decantr workspace health [--json] [--changed --since origin/main]
7316
7813
  decantr content-health [--json] [--markdown] [--ci]
7317
- decantr studio [--port 4319] [--host 127.0.0.1] [--report decantr-health.json]
7814
+ decantr studio [--port 4319] [--host 127.0.0.1] [--report decantr-health.json] [--workspace]
7318
7815
  decantr telemetry status [--json]
7319
7816
  decantr telemetry explain [--json]
7320
7817
  decantr telemetry link [--enable] [--org <slug>]
@@ -7334,7 +7831,7 @@ ${BOLD7}Init Options:${RESET14}
7334
7831
  --theme Theme ID
7335
7832
  --mode Color mode: dark | light | auto
7336
7833
  --shape Border shape: pill | rounded | sharp
7337
- --target Framework: react | vue | svelte | angular | nextjs | nuxt | astro | html
7834
+ --target Framework: react | vue | svelte | angular | solid | nextjs | nuxt | astro | html
7338
7835
  --guard Guard mode: creative | guided | strict
7339
7836
  --density Spacing: compact | comfortable | spacious
7340
7837
  --shell Default shell layout
@@ -7357,6 +7854,7 @@ ${BOLD7}Commands:${RESET14}
7357
7854
  ${cyan3("init")} Attach Decantr contract/context files to an existing project or empty workspace
7358
7855
  ${cyan3("status")} Show project status, DNA axioms, and blueprint info
7359
7856
  ${cyan3("health")} Generate a local Project Health report [--json] [--markdown] [--ci]; use health init-ci to install a GitHub Actions gate
7857
+ ${cyan3("workspace")} Discover and aggregate health across Decantr projects in a monorepo
7360
7858
  ${cyan3("content-health")} Generate a local registry content health report [--json] [--markdown] [--ci]
7361
7859
  ${cyan3("studio")} Open a local Project Health dashboard backed by the same report
7362
7860
  ${cyan3("sync")} Sync registry content from API
@@ -7400,6 +7898,9 @@ ${BOLD7}Examples:${RESET14}
7400
7898
  decantr health init-ci
7401
7899
  decantr health init-ci --project apps/web
7402
7900
  decantr health --ci --fail-on error
7901
+ decantr health --evidence --output .decantr/evidence/latest.json
7902
+ decantr workspace list
7903
+ decantr workspace health --changed --since origin/main
7403
7904
  decantr content-health --ci --fail-on error
7404
7905
  decantr studio
7405
7906
  decantr studio --report decantr-health.json
@@ -7433,7 +7934,7 @@ ${BOLD7}Workflow Model:${RESET14}
7433
7934
  ${cyan3("Hybrid composition")} decantr add/remove, decantr theme switch, decantr registry, decantr upgrade
7434
7935
 
7435
7936
  ${BOLD7}Bootstrap adapters:${RESET14}
7436
- Runnable starter adapters: ${cyan3("react-vite")}, ${cyan3("next-app")}
7937
+ Runnable starter adapters: ${cyan3("react-vite")}, ${cyan3("next-app")}, ${cyan3("vanilla-vite")}, ${cyan3("vue-vite")}, ${cyan3("sveltekit")}, ${cyan3("angular")}, ${cyan3("solid-vite")}
7437
7938
  Unsupported targets resolve through ${cyan3("generic-web")} contract-only mode until their starter adapters land.
7438
7939
  `);
7439
7940
  }
@@ -7468,7 +7969,8 @@ ${BOLD7}Usage:${RESET14}
7468
7969
  decantr health --markdown
7469
7970
  decantr health --ci [--fail-on error|warn|none]
7470
7971
  decantr health --prompt <finding-id>
7471
- decantr health init-ci [--force] [--project <path>] [--fail-on error|warn|none] [--cli-version <version|latest>]
7972
+ decantr health --evidence [--browser] [--design-tokens <path>]
7973
+ decantr health init-ci [--force] [--project <path>] [--workspace] [--fail-on error|warn|none] [--cli-version <version|latest>]
7472
7974
 
7473
7975
  ${BOLD7}Options:${RESET14}
7474
7976
  --format Output format: text, json, or markdown
@@ -7478,6 +7980,9 @@ ${BOLD7}Options:${RESET14}
7478
7980
  --ci Enable CI exit-code behavior
7479
7981
  --fail-on CI threshold: error, warn, or none
7480
7982
  --prompt Print an AI-ready remediation prompt for a finding
7983
+ --evidence Emit a local Evidence Bundle JSON artifact
7984
+ --browser Include optional rendered-browser setup/evidence checks
7985
+ --design-tokens Compare against a Figma/Tokens Studio JSON export
7481
7986
 
7482
7987
  ${BOLD7}Examples:${RESET14}
7483
7988
  decantr health
@@ -7485,7 +7990,25 @@ ${BOLD7}Examples:${RESET14}
7485
7990
  decantr health --markdown --output decantr-health.md
7486
7991
  decantr health --ci --fail-on error
7487
7992
  decantr health --prompt audit-essence-missing
7993
+ decantr health --evidence --output .decantr/evidence/latest.json
7488
7994
  decantr health init-ci --project apps/web
7995
+ decantr health init-ci --workspace
7996
+ `);
7997
+ }
7998
+ function cmdWorkspaceHelp() {
7999
+ console.log(`
8000
+ ${BOLD7}decantr workspace${RESET14} \u2014 Inspect Decantr projects across a monorepo
8001
+
8002
+ ${BOLD7}Usage:${RESET14}
8003
+ decantr workspace list [--json]
8004
+ decantr workspace health [--json|--markdown] [--output <file>]
8005
+ decantr workspace health --changed --since origin/main
8006
+
8007
+ ${BOLD7}Examples:${RESET14}
8008
+ decantr workspace list
8009
+ decantr workspace health
8010
+ decantr workspace health --json --output .decantr/workspace-health.json
8011
+ decantr workspace health --changed --since origin/main
7489
8012
  `);
7490
8013
  }
7491
8014
  function cmdContentHealthHelp() {
@@ -7526,6 +8049,7 @@ ${BOLD7}Options:${RESET14}
7526
8049
  --port Local port to bind; defaults to 4319
7527
8050
  --host Local host to bind; defaults to 127.0.0.1
7528
8051
  --report Serve a read-only Project Health JSON artifact instead of scanning the current project
8052
+ --workspace Serve a monorepo workspace health dashboard
7529
8053
 
7530
8054
  ${BOLD7}Endpoints:${RESET14}
7531
8055
  GET /
@@ -7538,6 +8062,7 @@ ${BOLD7}Examples:${RESET14}
7538
8062
  decantr studio --host 127.0.0.1 --port 4319
7539
8063
  decantr health --json --output decantr-health.json
7540
8064
  decantr studio --report decantr-health.json
8065
+ decantr studio --workspace
7541
8066
  `);
7542
8067
  }
7543
8068
  async function main() {
@@ -7653,7 +8178,7 @@ async function main() {
7653
8178
  break;
7654
8179
  }
7655
8180
  case "upgrade": {
7656
- const { cmdUpgrade } = await import("./upgrade-PL755AF7.js");
8181
+ const { cmdUpgrade } = await import("./upgrade-XTTGHFG7.js");
7657
8182
  const applyFlag = args.includes("--apply");
7658
8183
  await cmdUpgrade(process.cwd(), { apply: applyFlag });
7659
8184
  break;
@@ -7677,7 +8202,7 @@ async function main() {
7677
8202
  cmdHealthHelp();
7678
8203
  break;
7679
8204
  }
7680
- const { cmdHealth, parseHealthArgs } = await import("./health-EENY3BFS.js");
8205
+ const { cmdHealth, parseHealthArgs } = await import("./health-ZXOPGNBZ.js");
7681
8206
  await cmdHealth(process.cwd(), parseHealthArgs(args));
7682
8207
  } catch (e) {
7683
8208
  console.error(error3(e.message));
@@ -7705,7 +8230,7 @@ async function main() {
7705
8230
  cmdStudioHelp();
7706
8231
  break;
7707
8232
  }
7708
- const { cmdStudio, parseStudioArgs } = await import("./studio-TBJPZZHA.js");
8233
+ const { cmdStudio, parseStudioArgs } = await import("./studio-LHQXHBE7.js");
7709
8234
  await cmdStudio(process.cwd(), parseStudioArgs(args));
7710
8235
  } catch (e) {
7711
8236
  console.error(error3(e.message));
@@ -7713,6 +8238,20 @@ async function main() {
7713
8238
  }
7714
8239
  break;
7715
8240
  }
8241
+ case "workspace": {
8242
+ try {
8243
+ if (isCommandHelpRequest(args)) {
8244
+ cmdWorkspaceHelp();
8245
+ break;
8246
+ }
8247
+ const { cmdWorkspace } = await import("./workspace-MOLAGT2B.js");
8248
+ await cmdWorkspace(process.cwd(), args);
8249
+ } catch (e) {
8250
+ console.error(error3(e.message));
8251
+ process.exitCode = 1;
8252
+ }
8253
+ break;
8254
+ }
7716
8255
  case "migrate": {
7717
8256
  await cmdMigrate(process.cwd(), args.slice(1));
7718
8257
  break;
@@ -8199,7 +8738,7 @@ async function main() {
8199
8738
  exportOutput = args[i].split("=")[1];
8200
8739
  }
8201
8740
  }
8202
- const validTargets = ["shadcn", "tailwind", "css-vars"];
8741
+ const validTargets = ["shadcn", "tailwind", "css-vars", "figma-tokens"];
8203
8742
  if (!exportTarget || !validTargets.includes(exportTarget)) {
8204
8743
  console.error(error3(`Usage: decantr export --to <${validTargets.join("|")}>`));
8205
8744
  process.exitCode = 1;