@decantr/cli 2.13.1 → 3.0.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.
@@ -22,14 +22,37 @@ import {
22
22
  listWorkspaceCandidates,
23
23
  listWorkspaceProjects,
24
24
  shouldFailWorkspaceHealth
25
- } from "./chunk-YBSBAJ3E.js";
25
+ } from "./chunk-B2PJDAMS.js";
26
26
  import {
27
+ acceptBrownfieldLocalLaw,
28
+ acceptStyleBridge,
29
+ buildGraphArtifacts,
30
+ changedFiles,
31
+ cmdGraph,
32
+ cmdGraphHelp,
33
+ createBrownfieldCodifyProposal,
34
+ createLocalLawTaskSummary,
27
35
  createProjectHealthReport,
36
+ createStyleBridgeProposal,
37
+ createStyleBridgeTaskSummary,
28
38
  formatProjectHealthMarkdown,
29
39
  formatProjectHealthText,
40
+ localPatternsPath,
41
+ localPatternsProposalPath,
42
+ localRulesPath,
43
+ localRulesProposalPath,
44
+ readLocalPatternPack,
30
45
  resolveWorkspaceInfo,
31
- shouldFailHealth
32
- } from "./chunk-3A2DLR47.js";
46
+ routeImpacts,
47
+ shouldFailHealth,
48
+ styleBridgeMatches,
49
+ styleBridgePath,
50
+ styleBridgeProposalPath,
51
+ validateLocalLaw,
52
+ writeBrownfieldCodifyProposal,
53
+ writeHostedPatternMappingProposal,
54
+ writeStyleBridgeProposal
55
+ } from "./chunk-4NDOHCYY.js";
33
56
  import {
34
57
  buildGuardRegistryContext,
35
58
  createDoctrineMap,
@@ -46,13 +69,19 @@ import {
46
69
  sendCliCommandTelemetry,
47
70
  sendNewProjectCompletedTelemetry,
48
71
  writeDoctrineMap
49
- } from "./chunk-MNZKOZW7.js";
72
+ } from "./chunk-7Z74ETDR.js";
50
73
 
51
74
  // src/index.ts
52
- import { existsSync as existsSync30, mkdirSync as mkdirSync17, readdirSync as readdirSync9, readFileSync as readFileSync23, writeFileSync as writeFileSync20 } from "fs";
53
- import { basename as basename3, dirname as dirname6, isAbsolute as isAbsolute3, join as join32, relative as relative7, resolve as resolve4 } from "path";
75
+ import { existsSync as existsSync28, mkdirSync as mkdirSync15, readdirSync as readdirSync8, readFileSync as readFileSync21, writeFileSync as writeFileSync18 } from "fs";
76
+ import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join30, relative as relative6, resolve as resolve4 } from "path";
54
77
  import { fileURLToPath as fileURLToPath3 } from "url";
55
- import { evaluateGuard, isV4 as isV49, validateEssence as validateEssence2 } from "@decantr/essence-spec";
78
+ import {
79
+ buildGraphImpactContext,
80
+ buildGraphRouteContext,
81
+ graphPayloadString,
82
+ summarizeGraphDiff
83
+ } from "@decantr/core";
84
+ import { evaluateGuard, isV4 as isV48, validateEssence as validateEssence2 } from "@decantr/essence-spec";
56
85
  import {
57
86
  CONTENT_TYPE_TO_API_CONTENT_TYPE as CONTENT_TYPE_TO_API_CONTENT_TYPE3,
58
87
  CONTENT_TYPES as GET_CONTENT_TYPES,
@@ -538,7 +567,10 @@ var solidPackagePlan = {
538
567
  function writeReactViteProject(projectDir, title, routingMode) {
539
568
  const srcDir = join4(projectDir, "src");
540
569
  const routerImport = routingMode === "hash" ? "HashRouter" : "BrowserRouter";
541
- writeJson(join4(projectDir, "package.json"), packagePlanPackageJson(projectDir, "decantr-app", reactVitePackagePlan));
570
+ writeJson(
571
+ join4(projectDir, "package.json"),
572
+ packagePlanPackageJson(projectDir, "decantr-app", reactVitePackagePlan)
573
+ );
542
574
  writeFileSync4(
543
575
  join4(projectDir, "vite.config.ts"),
544
576
  `import { defineConfig } from 'vite';
@@ -550,7 +582,10 @@ export default defineConfig({
550
582
  `
551
583
  );
552
584
  writeViteTsConfig(projectDir, { jsx: "react-jsx" });
553
- writeJson(join4(projectDir, "tsconfig.app.json"), JSON.parse(readFileSync3(join4(projectDir, "tsconfig.json"), "utf-8")));
585
+ writeJson(
586
+ join4(projectDir, "tsconfig.app.json"),
587
+ JSON.parse(readFileSync3(join4(projectDir, "tsconfig.json"), "utf-8"))
588
+ );
554
589
  writeIndexHtml(projectDir, title, "/src/main.tsx");
555
590
  mkdirp(srcDir);
556
591
  writeFileSync4(
@@ -613,9 +648,18 @@ export function App() {
613
648
  function writeNextProject(projectDir, title) {
614
649
  const appDir = join4(projectDir, "app");
615
650
  const stylesDir = join4(projectDir, "src", "styles");
616
- writeJson(join4(projectDir, "package.json"), packagePlanPackageJson(projectDir, "decantr-next-app", nextPackagePlan));
617
- writeFileSync4(join4(projectDir, "next.config.ts"), 'import type { NextConfig } from "next";\n\nconst nextConfig: NextConfig = {};\n\nexport default nextConfig;\n');
618
- writeFileSync4(join4(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');
651
+ writeJson(
652
+ join4(projectDir, "package.json"),
653
+ packagePlanPackageJson(projectDir, "decantr-next-app", nextPackagePlan)
654
+ );
655
+ writeFileSync4(
656
+ join4(projectDir, "next.config.ts"),
657
+ 'import type { NextConfig } from "next";\n\nconst nextConfig: NextConfig = {};\n\nexport default nextConfig;\n'
658
+ );
659
+ writeFileSync4(
660
+ join4(projectDir, "next-env.d.ts"),
661
+ '/// <reference types="next" />\n/// <reference types="next/image-types/global" />\n\n// This file is generated by Next.js.\n'
662
+ );
619
663
  writeJson(join4(projectDir, "tsconfig.json"), {
620
664
  compilerOptions: {
621
665
  target: "ES2017",
@@ -682,7 +726,10 @@ export default function HomePage() {
682
726
  }
683
727
  function writeVanillaProject(projectDir, title, routingMode) {
684
728
  const srcDir = join4(projectDir, "src");
685
- writeJson(join4(projectDir, "package.json"), packagePlanPackageJson(projectDir, "decantr-vanilla-app", vanillaPackagePlan));
729
+ writeJson(
730
+ join4(projectDir, "package.json"),
731
+ packagePlanPackageJson(projectDir, "decantr-vanilla-app", vanillaPackagePlan)
732
+ );
686
733
  writeIndexHtml(projectDir, title, "/src/main.js");
687
734
  mkdirp(srcDir);
688
735
  writeFileSync4(
@@ -718,14 +765,23 @@ root.innerHTML = \`
718
765
  function writeVueProject(projectDir, title, routingMode) {
719
766
  const srcDir = join4(projectDir, "src");
720
767
  const historyFactory = routingMode === "hash" ? "createWebHashHistory" : "createWebHistory";
721
- writeJson(join4(projectDir, "package.json"), packagePlanPackageJson(projectDir, "decantr-vue-app", vuePackagePlan));
722
- writeFileSync4(join4(projectDir, "vite.config.ts"), `import { defineConfig } from 'vite';
768
+ writeJson(
769
+ join4(projectDir, "package.json"),
770
+ packagePlanPackageJson(projectDir, "decantr-vue-app", vuePackagePlan)
771
+ );
772
+ writeFileSync4(
773
+ join4(projectDir, "vite.config.ts"),
774
+ `import { defineConfig } from 'vite';
723
775
  import vue from '@vitejs/plugin-vue';
724
776
 
725
777
  export default defineConfig({ plugins: [vue()] });
726
- `);
778
+ `
779
+ );
727
780
  writeViteTsConfig(projectDir);
728
- writeFileSync4(join4(projectDir, "env.d.ts"), '/// <reference types="vite/client" />\n\ndeclare module "*.vue";\n');
781
+ writeFileSync4(
782
+ join4(projectDir, "env.d.ts"),
783
+ '/// <reference types="vite/client" />\n\ndeclare module "*.vue";\n'
784
+ );
729
785
  writeIndexHtml(projectDir, title, "/src/main.ts");
730
786
  mkdirp(srcDir);
731
787
  writeFileSync4(
@@ -773,21 +829,35 @@ import { css } from '@decantr/css';
773
829
  }
774
830
  function writeSvelteProject(projectDir, title) {
775
831
  const routesDir = join4(projectDir, "src", "routes");
776
- writeJson(join4(projectDir, "package.json"), packagePlanPackageJson(projectDir, "decantr-svelte-app", sveltePackagePlan));
777
- writeFileSync4(join4(projectDir, "svelte.config.js"), `import adapter from '@sveltejs/adapter-auto';
832
+ writeJson(
833
+ join4(projectDir, "package.json"),
834
+ packagePlanPackageJson(projectDir, "decantr-svelte-app", sveltePackagePlan)
835
+ );
836
+ writeFileSync4(
837
+ join4(projectDir, "svelte.config.js"),
838
+ `import adapter from '@sveltejs/adapter-auto';
778
839
  import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
779
840
 
780
841
  export default { preprocess: vitePreprocess(), kit: { adapter: adapter() } };
781
- `);
782
- writeFileSync4(join4(projectDir, "vite.config.ts"), `import { sveltekit } from '@sveltejs/kit/vite';
842
+ `
843
+ );
844
+ writeFileSync4(
845
+ join4(projectDir, "vite.config.ts"),
846
+ `import { sveltekit } from '@sveltejs/kit/vite';
783
847
  import { defineConfig } from 'vite';
784
848
 
785
849
  export default defineConfig({ plugins: [sveltekit()] });
786
- `);
850
+ `
851
+ );
787
852
  writeViteTsConfig(projectDir);
788
853
  mkdirp(routesDir);
789
- writeFileSync4(join4(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');
790
- writeFileSync4(join4(routesDir, "+layout.svelte"), `<script lang="ts">
854
+ writeFileSync4(
855
+ join4(projectDir, "src", "app.html"),
856
+ '<!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'
857
+ );
858
+ writeFileSync4(
859
+ join4(routesDir, "+layout.svelte"),
860
+ `<script lang="ts">
791
861
  import '../styles/global.css';
792
862
  import '../styles/tokens.css';
793
863
  import '../styles/treatments.css';
@@ -795,7 +865,8 @@ let { children } = $props();
795
865
  </script>
796
866
 
797
867
  {@render children()}
798
- `);
868
+ `
869
+ );
799
870
  writeFileSync4(
800
871
  join4(routesDir, "+page.svelte"),
801
872
  `<script lang="ts">
@@ -823,7 +894,10 @@ import { css } from '@decantr/css';
823
894
  }
824
895
  function writeAngularProject(projectDir, title) {
825
896
  const appDir = join4(projectDir, "src", "app");
826
- writeJson(join4(projectDir, "package.json"), packagePlanPackageJson(projectDir, "decantr-angular-app", angularPackagePlan));
897
+ writeJson(
898
+ join4(projectDir, "package.json"),
899
+ packagePlanPackageJson(projectDir, "decantr-angular-app", angularPackagePlan)
900
+ );
827
901
  writeJson(join4(projectDir, "angular.json"), {
828
902
  version: 1,
829
903
  projects: {
@@ -862,27 +936,40 @@ function writeAngularProject(projectDir, title) {
862
936
  lib: ["ES2022", "DOM"]
863
937
  }
864
938
  });
865
- writeJson(join4(projectDir, "tsconfig.app.json"), { extends: "./tsconfig.json", files: ["src/main.ts"], include: ["src/**/*.d.ts"] });
939
+ writeJson(join4(projectDir, "tsconfig.app.json"), {
940
+ extends: "./tsconfig.json",
941
+ files: ["src/main.ts"],
942
+ include: ["src/**/*.d.ts"]
943
+ });
866
944
  mkdirp(appDir);
867
- writeFileSync4(join4(projectDir, "src", "index.html"), `<!doctype html>
945
+ writeFileSync4(
946
+ join4(projectDir, "src", "index.html"),
947
+ `<!doctype html>
868
948
  <html lang="en">
869
949
  <head><meta charset="utf-8"><title>${title}</title><meta name="viewport" content="width=device-width, initial-scale=1"></head>
870
950
  <body><app-root></app-root></body>
871
951
  </html>
872
- `);
873
- writeFileSync4(join4(projectDir, "src", "main.ts"), `import { bootstrapApplication } from '@angular/platform-browser';
952
+ `
953
+ );
954
+ writeFileSync4(
955
+ join4(projectDir, "src", "main.ts"),
956
+ `import { bootstrapApplication } from '@angular/platform-browser';
874
957
  import { provideRouter } from '@angular/router';
875
958
  import { AppComponent } from './app/app.component';
876
959
  import { routes } from './app/app.routes';
877
960
 
878
961
  bootstrapApplication(AppComponent, { providers: [provideRouter(routes)] }).catch((err) => console.error(err));
879
- `);
962
+ `
963
+ );
880
964
  writeFileSync4(join4(projectDir, "src", "styles.css"), '@import "./styles/global.css";\n');
881
- writeFileSync4(join4(appDir, "app.routes.ts"), `import type { Routes } from '@angular/router';
965
+ writeFileSync4(
966
+ join4(appDir, "app.routes.ts"),
967
+ `import type { Routes } from '@angular/router';
882
968
  import { AppComponent } from './app.component';
883
969
 
884
970
  export const routes: Routes = [{ path: '', component: AppComponent }];
885
- `);
971
+ `
972
+ );
886
973
  writeFileSync4(
887
974
  join4(appDir, "app.component.ts"),
888
975
  `import { Component } from '@angular/core';
@@ -912,14 +999,23 @@ export class AppComponent {}
912
999
  function writeSolidProject(projectDir, title, routingMode) {
913
1000
  const srcDir = join4(projectDir, "src");
914
1001
  const routerImport = routingMode === "hash" ? "HashRouter" : "Router";
915
- writeJson(join4(projectDir, "package.json"), packagePlanPackageJson(projectDir, "decantr-solid-app", solidPackagePlan));
916
- writeFileSync4(join4(projectDir, "vite.config.ts"), `import { defineConfig } from 'vite';
1002
+ writeJson(
1003
+ join4(projectDir, "package.json"),
1004
+ packagePlanPackageJson(projectDir, "decantr-solid-app", solidPackagePlan)
1005
+ );
1006
+ writeFileSync4(
1007
+ join4(projectDir, "vite.config.ts"),
1008
+ `import { defineConfig } from 'vite';
917
1009
  import solid from 'vite-plugin-solid';
918
1010
 
919
1011
  export default defineConfig({ plugins: [solid()] });
920
- `);
1012
+ `
1013
+ );
921
1014
  writeViteTsConfig(projectDir, { jsx: "preserve" });
922
- writeJson(join4(projectDir, "tsconfig.app.json"), JSON.parse(readFileSync3(join4(projectDir, "tsconfig.json"), "utf-8")));
1015
+ writeJson(
1016
+ join4(projectDir, "tsconfig.app.json"),
1017
+ JSON.parse(readFileSync3(join4(projectDir, "tsconfig.json"), "utf-8"))
1018
+ );
923
1019
  writeIndexHtml(projectDir, title, "/src/main.tsx");
924
1020
  mkdirp(srcDir);
925
1021
  writeFileSync4(
@@ -990,7 +1086,10 @@ var DECANTR_ADAPTERS = {
990
1086
  },
991
1087
  verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: "dist" },
992
1088
  packagePlan: reactVitePackagePlan,
993
- docs: { summary: "React + Vite runnable starter.", notes: ["Uses React Router and @decantr/css."] },
1089
+ docs: {
1090
+ summary: "React + Vite runnable starter.",
1091
+ notes: ["Uses React Router and @decantr/css."]
1092
+ },
994
1093
  writeProjectFiles: writeReactViteProject
995
1094
  },
996
1095
  "next-app": {
@@ -1009,7 +1108,10 @@ var DECANTR_ADAPTERS = {
1009
1108
  },
1010
1109
  verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: ".next" },
1011
1110
  packagePlan: nextPackagePlan,
1012
- docs: { summary: "Next.js App Router runnable starter.", notes: ["Uses App Router for greenfield bootstraps."] },
1111
+ docs: {
1112
+ summary: "Next.js App Router runnable starter.",
1113
+ notes: ["Uses App Router for greenfield bootstraps."]
1114
+ },
1013
1115
  writeProjectFiles: writeNextProject
1014
1116
  },
1015
1117
  "vanilla-vite": {
@@ -1028,7 +1130,10 @@ var DECANTR_ADAPTERS = {
1028
1130
  },
1029
1131
  verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: "dist" },
1030
1132
  packagePlan: vanillaPackagePlan,
1031
- docs: { summary: "Plain web runnable starter.", notes: ["Uses DOM APIs and @decantr/css without a UI framework."] },
1133
+ docs: {
1134
+ summary: "Plain web runnable starter.",
1135
+ notes: ["Uses DOM APIs and @decantr/css without a UI framework."]
1136
+ },
1032
1137
  writeProjectFiles: writeVanillaProject
1033
1138
  },
1034
1139
  "vue-vite": {
@@ -1047,7 +1152,10 @@ var DECANTR_ADAPTERS = {
1047
1152
  },
1048
1153
  verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: "dist" },
1049
1154
  packagePlan: vuePackagePlan,
1050
- docs: { summary: "Vue 3 + Vite runnable starter.", notes: ["Uses Vue Router and @decantr/css."] },
1155
+ docs: {
1156
+ summary: "Vue 3 + Vite runnable starter.",
1157
+ notes: ["Uses Vue Router and @decantr/css."]
1158
+ },
1051
1159
  writeProjectFiles: writeVueProject
1052
1160
  },
1053
1161
  sveltekit: {
@@ -1066,7 +1174,10 @@ var DECANTR_ADAPTERS = {
1066
1174
  },
1067
1175
  verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: ".svelte-kit" },
1068
1176
  packagePlan: sveltePackagePlan,
1069
- docs: { summary: "SvelteKit runnable starter.", notes: ["Uses SvelteKit file routing and @decantr/css."] },
1177
+ docs: {
1178
+ summary: "SvelteKit runnable starter.",
1179
+ notes: ["Uses SvelteKit file routing and @decantr/css."]
1180
+ },
1070
1181
  writeProjectFiles: writeSvelteProject
1071
1182
  },
1072
1183
  angular: {
@@ -1085,7 +1196,10 @@ var DECANTR_ADAPTERS = {
1085
1196
  },
1086
1197
  verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: "dist" },
1087
1198
  packagePlan: angularPackagePlan,
1088
- docs: { summary: "Angular standalone runnable starter.", notes: ["Uses Angular Router and global Decantr CSS files."] },
1199
+ docs: {
1200
+ summary: "Angular standalone runnable starter.",
1201
+ notes: ["Uses Angular Router and global Decantr CSS files."]
1202
+ },
1089
1203
  writeProjectFiles: writeAngularProject
1090
1204
  },
1091
1205
  "solid-vite": {
@@ -1104,7 +1218,10 @@ var DECANTR_ADAPTERS = {
1104
1218
  },
1105
1219
  verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: "dist" },
1106
1220
  packagePlan: solidPackagePlan,
1107
- docs: { summary: "Solid + Vite runnable starter.", notes: ["Uses @solidjs/router and @decantr/css."] },
1221
+ docs: {
1222
+ summary: "Solid + Vite runnable starter.",
1223
+ notes: ["Uses @solidjs/router and @decantr/css."]
1224
+ },
1108
1225
  writeProjectFiles: writeSolidProject
1109
1226
  },
1110
1227
  "generic-web": {
@@ -1122,7 +1239,10 @@ var DECANTR_ADAPTERS = {
1122
1239
  componentRoots: ["src/components", "components"]
1123
1240
  },
1124
1241
  verify: { devCommand: "npm run dev", buildCommand: "npm run build", distDir: "dist" },
1125
- docs: { summary: "Contract-only fallback for unsupported web targets.", notes: ["Does not write framework runtime files."] }
1242
+ docs: {
1243
+ summary: "Contract-only fallback for unsupported web targets.",
1244
+ notes: ["Does not write framework runtime files."]
1245
+ }
1126
1246
  }
1127
1247
  };
1128
1248
  var TARGET_ALIASES = /* @__PURE__ */ new Map();
@@ -3235,1107 +3355,8 @@ ${YELLOW2}Next step:${RESET2} Review ${BOLD}${reportDisplayPath}${RESET2}, then
3235
3355
  }
3236
3356
 
3237
3357
  // src/commands/ci.ts
3238
- import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "fs";
3239
- import { dirname as dirname3, join as join17, relative as relative4, resolve } from "path";
3240
-
3241
- // src/local-law.ts
3242
- import { execFileSync } from "child_process";
3243
- import { existsSync as existsSync13, mkdirSync as mkdirSync8, readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync5, writeFileSync as writeFileSync9 } from "fs";
3244
- import { basename as basename2, extname, join as join15, relative as relative3, sep } from "path";
3245
- import { isV4 as isV43 } from "@decantr/essence-spec";
3246
- var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
3247
- ".astro",
3248
- ".html",
3249
- ".js",
3250
- ".jsx",
3251
- ".svelte",
3252
- ".ts",
3253
- ".tsx",
3254
- ".vue"
3255
- ]);
3256
- var UI_TEMPLATE_EXTENSIONS = /* @__PURE__ */ new Set([".astro", ".html", ".jsx", ".svelte", ".tsx", ".vue"]);
3257
- var IGNORED_DIRS = /* @__PURE__ */ new Set([
3258
- ".decantr",
3259
- ".git",
3260
- ".next",
3261
- ".nuxt",
3262
- ".svelte-kit",
3263
- "build",
3264
- "coverage",
3265
- "dist",
3266
- "node_modules",
3267
- "out"
3268
- ]);
3269
- var DEFAULT_RULE_EXTENSIONS = [".astro", ".html", ".jsx", ".svelte", ".tsx", ".vue"];
3270
- function localPatternsProposalPath(projectRoot) {
3271
- return join15(projectRoot, ".decantr", "local-patterns.proposal.json");
3272
- }
3273
- function localPatternsPath(projectRoot) {
3274
- return join15(projectRoot, ".decantr", "local-patterns.json");
3275
- }
3276
- function localRulesProposalPath(projectRoot) {
3277
- return join15(projectRoot, ".decantr", "rules.proposal.json");
3278
- }
3279
- function localRulesPath(projectRoot) {
3280
- return join15(projectRoot, ".decantr", "rules.json");
3281
- }
3282
- function readLocalPatternPack(projectRoot) {
3283
- return readJsonFile(localPatternsPath(projectRoot));
3284
- }
3285
- function readLocalRuleManifest(projectRoot) {
3286
- return readJsonFile(localRulesPath(projectRoot));
3287
- }
3288
- function createBrownfieldCodifyProposal(input) {
3289
- const sourceFiles = input.fromAudit ? listSourceFiles(input.projectRoot, 800) : [];
3290
- const evidence = summarizeSourceEvidence(input.projectRoot, sourceFiles);
3291
- const routes = input.essence && isV43(input.essence) ? Object.keys(input.essence.blueprint.routes ?? {}).sort() : [];
3292
- const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
3293
- const patternPack = {
3294
- version: 2,
3295
- generatedAt,
3296
- status: "proposal",
3297
- source: input.fromAudit ? "decantr codify --from-audit" : "decantr codify",
3298
- project: {
3299
- framework: input.detected.framework,
3300
- packageManager: input.detected.packageManager,
3301
- hasTailwind: input.detected.hasTailwind,
3302
- ruleFiles: input.detected.existingRuleFiles,
3303
- routeCount: routes.length
3304
- },
3305
- purpose: "Project-owned Brownfield/Hybrid UI law. Review and edit before accepting; Decantr treats this as authoritative only after it is copied to .decantr/local-patterns.json.",
3306
- hybrid: {
3307
- intent: "This local pattern pack is the first Hybrid authority layer: it maps Decantr concepts onto project-owned components, tokens, classes, and rules without replacing the app runtime.",
3308
- authorityPrecedence: [
3309
- "existing production source",
3310
- "accepted local patterns and rules",
3311
- "Decantr Essence V4 contract",
3312
- "hosted registry patterns and execution packs as optional guidance"
3313
- ],
3314
- hostedPatternMapping: "Use hosted patterns as vocabulary and review guidance. Before enforcing one, map it to a project-owned component path, class recipe, token recipe, or explicit exception here."
3315
- },
3316
- patterns: [
3317
- {
3318
- id: "button",
3319
- label: "Button primitives",
3320
- role: "Actions and command triggers",
3321
- appliesTo: [
3322
- "primary action",
3323
- "secondary action",
3324
- "tertiary action",
3325
- "destructive action",
3326
- "icon action"
3327
- ],
3328
- componentPaths: evidence.buttonComponents,
3329
- decide: "Define primary, secondary, tertiary, destructive, icon-only, disabled, and loading button variants from this app.",
3330
- classHints: evidence.buttonClassHints,
3331
- variants: evidence.buttonVariants,
3332
- sourceEvidence: evidence.buttonSourceEvidence,
3333
- confidence: confidenceForEvidence({
3334
- componentCount: evidence.buttonComponents.length,
3335
- classHintCount: evidence.buttonClassHints.length,
3336
- variantCount: evidence.buttonVariants.length,
3337
- sourceEvidenceCount: evidence.buttonSourceEvidence.length
3338
- }),
3339
- enforcement: {
3340
- level: "warn",
3341
- status: "proposal",
3342
- notes: [
3343
- "Treat as advisory until the team confirms variant names and wrapper paths.",
3344
- "Raise related rules to error only after raw button usage can be fixed without churn."
3345
- ]
3346
- },
3347
- evidence: evidence.buttonComponents.length ? evidence.buttonComponents : evidence.buttonClassHints.length ? evidence.buttonClassHints : [
3348
- "No obvious Button wrapper found yet. Add the project-owned wrapper path before strict enforcement."
3349
- ],
3350
- forbiddenAlternatives: ["New one-off button variants without updating this manifest."]
3351
- },
3352
- {
3353
- id: "surface-card",
3354
- label: "Cards and surfaces",
3355
- role: "Cards, panels, and reusable content surfaces",
3356
- appliesTo: ["cards", "panels", "modals", "list items", "dashboard tiles"],
3357
- componentPaths: evidence.cardComponents,
3358
- decide: "Define the canonical card background, border, radius, shadow, padding, density, and hover treatment.",
3359
- classHints: evidence.cardClassHints,
3360
- variants: evidence.cardVariants,
3361
- sourceEvidence: evidence.cardSourceEvidence,
3362
- confidence: confidenceForEvidence({
3363
- componentCount: evidence.cardComponents.length,
3364
- classHintCount: evidence.cardClassHints.length,
3365
- variantCount: evidence.cardVariants.length,
3366
- sourceEvidenceCount: evidence.cardSourceEvidence.length
3367
- }),
3368
- enforcement: {
3369
- level: "warn",
3370
- status: "proposal",
3371
- notes: [
3372
- "Use this family to converge repeated surface recipes before enforcing card rules.",
3373
- "Document route-specific card exceptions before asking CI to block them."
3374
- ]
3375
- },
3376
- evidence: evidence.cardComponents.length ? evidence.cardComponents : [
3377
- "No obvious Card wrapper found yet. Add the project-owned wrapper path or class recipe."
3378
- ],
3379
- forbiddenAlternatives: ["Flat ad hoc cards with unique color/radius/shadow recipes."]
3380
- },
3381
- {
3382
- id: "page-shell",
3383
- label: "Page shell and spacing",
3384
- role: "Route shell, navigation, gutters, max-width, and scroll ownership",
3385
- appliesTo: ["routes", "layouts", "navigation shells", "scroll containers"],
3386
- componentPaths: evidence.shellComponents,
3387
- decide: "Define which layout owns max width, gutters, sticky chrome, responsive breakpoints, and scroll containers.",
3388
- sourceEvidence: collectSourceEvidence(input.projectRoot, sourceFiles, [
3389
- "layout",
3390
- "shell",
3391
- "nav",
3392
- "sidebar",
3393
- "container"
3394
- ]),
3395
- confidence: confidenceForEvidence({
3396
- componentCount: evidence.shellComponents.length,
3397
- sourceEvidenceCount: evidence.shellComponents.length
3398
- }),
3399
- enforcement: {
3400
- level: "advisory",
3401
- status: "proposal",
3402
- notes: [
3403
- "Layout ownership is usually reviewed by humans first because route shells can legitimately differ."
3404
- ]
3405
- },
3406
- evidence: evidence.shellComponents.length ? evidence.shellComponents : ["Add root layout, shell, or app frame files that establish route chrome and spacing."],
3407
- forbiddenAlternatives: [
3408
- "Each page inventing independent max-width, padding, or sticky nav rules."
3409
- ]
3410
- },
3411
- {
3412
- id: "form-control",
3413
- label: "Form controls",
3414
- role: "Inputs, labels, validation, and form actions",
3415
- appliesTo: ["inputs", "selects", "textareas", "validation messages", "form actions"],
3416
- componentPaths: evidence.formComponents,
3417
- decide: "Define input height, label placement, error copy, disabled state, required state, and focus treatment.",
3418
- variants: evidence.formVariants,
3419
- sourceEvidence: evidence.formSourceEvidence,
3420
- confidence: confidenceForEvidence({
3421
- componentCount: evidence.formComponents.length,
3422
- variantCount: evidence.formVariants.length,
3423
- sourceEvidenceCount: evidence.formSourceEvidence.length
3424
- }),
3425
- enforcement: {
3426
- level: "warn",
3427
- status: "proposal",
3428
- notes: [
3429
- "Start by mapping field wrappers, labels, error states, and disabled states from source."
3430
- ]
3431
- },
3432
- evidence: evidence.formComponents.length ? evidence.formComponents : ["Add form field wrapper paths and validation examples."],
3433
- forbiddenAlternatives: [
3434
- "Unlabeled one-off inputs or validation states that do not match the app standard."
3435
- ]
3436
- },
3437
- {
3438
- id: "theme-variant",
3439
- label: "Theme variants",
3440
- role: "Light, dark, brand, density, and tenant/theme variants observed in the app",
3441
- appliesTo: ["theme toggles", "mode-specific classes", "brand variants", "tenant variants"],
3442
- componentPaths: evidence.themeComponents,
3443
- decide: "Document which theme variants exist, where they are toggled, and which tokens/classes are legal per variant.",
3444
- variants: evidence.themeVariants,
3445
- sourceEvidence: evidence.themeSourceEvidence,
3446
- confidence: confidenceForEvidence({
3447
- componentCount: evidence.themeComponents.length,
3448
- variantCount: evidence.themeVariants.length,
3449
- sourceEvidenceCount: evidence.themeSourceEvidence.length
3450
- }),
3451
- enforcement: {
3452
- level: "advisory",
3453
- status: "proposal",
3454
- notes: [
3455
- "Theme variant law should preserve the existing theme provider/tokens before adding any new mode."
3456
- ]
3457
- },
3458
- evidence: evidence.themeComponents.length ? evidence.themeComponents : ["If the app has dark/light or brand variants, add the toggles/providers here."],
3459
- forbiddenAlternatives: [
3460
- "Component-local theme forks that bypass shared theme providers or tokens."
3461
- ]
3462
- }
3463
- ],
3464
- starterRules: [
3465
- "Prefer project-owned wrappers for repeated primitives once they exist.",
3466
- "Avoid raw hex/rgb values in component templates unless documented as dynamic data.",
3467
- "Avoid static inline styles for reusable visual treatment.",
3468
- "When adding a new route, map it to an existing local pattern before inventing a new visual variant.",
3469
- "When adding a theme variant, update .decantr/theme-inventory.json and this local pattern pack.",
3470
- "Map hosted Decantr patterns into project-owned local law before making them enforceable."
3471
- ],
3472
- nextSteps: [
3473
- "Edit this proposal with real component paths and token/class recipes.",
3474
- "Run decantr codify --accept after review.",
3475
- "Use decantr task <route> before LLM edits so local law appears in task context.",
3476
- "Run decantr verify --brownfield --local-patterns after edits.",
3477
- "For Hybrid adoption, start with warn-level local rules and raise severities only after the team agrees the law is stable.",
3478
- "Wire deterministic project rules into ESLint, Biome, Storybook, visual tests, or CI where Decantr should not guess."
3479
- ]
3480
- };
3481
- const ruleManifest = {
3482
- version: 1,
3483
- status: "proposal",
3484
- generatedAt,
3485
- source: input.fromAudit ? "decantr codify --from-audit" : "decantr codify",
3486
- purpose: "Mechanical Brownfield/Hybrid checks owned by this project. These rules are intentionally local and stack-agnostic; edit before accepting.",
3487
- enforcement: {
3488
- defaultSeverity: "warn",
3489
- mode: "warn",
3490
- notes: [
3491
- "Decantr local rules are a guardrail, not a replacement for ESLint, Biome, type checks, tests, or visual regression.",
3492
- "Keep rules narrow enough that an LLM can fix findings without rewriting the app.",
3493
- "Use error severity only after the team agrees the rule is stable."
3494
- ]
3495
- },
3496
- rules: [
3497
- {
3498
- id: "no-inline-style",
3499
- type: "forbid-regex",
3500
- enabled: true,
3501
- severity: "warn",
3502
- description: "Reusable UI should not add static inline style attributes.",
3503
- includeExtensions: DEFAULT_RULE_EXTENSIONS,
3504
- pattern: "\\bstyle\\s*=",
3505
- message: "Inline style found in UI template.",
3506
- suggestedFix: "Move reusable visual treatment into the project style system, component wrapper, token, or documented local pattern.",
3507
- maxFindings: 25
3508
- },
3509
- {
3510
- id: "no-raw-color-literals",
3511
- type: "forbid-regex",
3512
- enabled: true,
3513
- severity: "warn",
3514
- description: "Component templates should not introduce raw hex/rgb color literals.",
3515
- includeExtensions: DEFAULT_RULE_EXTENSIONS,
3516
- pattern: "#(?:[0-9a-fA-F]{3,8})\\b|rgba?\\s*\\(",
3517
- message: "Raw color literal found in UI template.",
3518
- suggestedFix: "Use an existing project token/class, or document the exception in .decantr/local-patterns.json if the value is data-driven.",
3519
- maxFindings: 25
3520
- },
3521
- {
3522
- id: "prefer-button-wrapper",
3523
- type: "forbid-regex",
3524
- enabled: evidence.buttonComponents.length > 0,
3525
- severity: "info",
3526
- description: "Prefer the project-owned button primitive instead of new raw button markup.",
3527
- includeExtensions: DEFAULT_RULE_EXTENSIONS,
3528
- pattern: "<button[\\s>]",
3529
- message: "Raw <button> usage found outside the detected button wrapper.",
3530
- suggestedFix: "Use the project-owned Button primitive, or add this file to allowedPaths if it is the primitive implementation.",
3531
- allowedPaths: evidence.buttonComponents,
3532
- maxFindings: 50
3533
- }
3534
- ]
3535
- };
3536
- return { patternPack, ruleManifest };
3537
- }
3538
- function writeBrownfieldCodifyProposal(projectRoot, proposal) {
3539
- const decantrDir = join15(projectRoot, ".decantr");
3540
- mkdirSync8(decantrDir, { recursive: true });
3541
- const patternPath = localPatternsProposalPath(projectRoot);
3542
- const rulesPath = localRulesProposalPath(projectRoot);
3543
- writeFileSync9(patternPath, `${JSON.stringify(proposal.patternPack, null, 2)}
3544
- `, "utf-8");
3545
- writeFileSync9(rulesPath, `${JSON.stringify(proposal.ruleManifest, null, 2)}
3546
- `, "utf-8");
3547
- return { patternPath, rulesPath };
3548
- }
3549
- function writeHostedPatternMappingProposal(input) {
3550
- const decantrDir = join15(input.projectRoot, ".decantr");
3551
- mkdirSync8(decantrDir, { recursive: true });
3552
- const existing = readJsonFile(localPatternsProposalPath(input.projectRoot)) ?? readJsonFile(localPatternsPath(input.projectRoot));
3553
- const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
3554
- const patternPack = {
3555
- version: 2,
3556
- generatedAt,
3557
- status: "proposal",
3558
- source: "decantr codify --map-pattern",
3559
- purpose: existing?.purpose ?? "Project-owned Brownfield/Hybrid UI law. Review and edit before accepting; Decantr treats this as authoritative only after it is copied to .decantr/local-patterns.json.",
3560
- hybrid: existing?.hybrid ?? {
3561
- intent: "This local pattern pack maps Decantr concepts onto project-owned components, tokens, classes, and rules without replacing the app runtime.",
3562
- authorityPrecedence: [
3563
- "existing production source",
3564
- "accepted local patterns and rules",
3565
- "Decantr Essence V4 contract",
3566
- "hosted registry patterns and execution packs as optional guidance"
3567
- ],
3568
- hostedPatternMapping: "Hosted registry patterns are advisory until mapped to project-owned component paths, token recipes, class recipes, and explicit exceptions."
3569
- },
3570
- patterns: Array.isArray(existing?.patterns) ? [...existing.patterns] : [],
3571
- starterRules: Array.isArray(existing?.starterRules) ? existing.starterRules : [],
3572
- nextSteps: Array.isArray(existing?.nextSteps) ? existing.nextSteps : [
3573
- "Review this proposal with the team.",
3574
- "Add project-owned component paths, token hints, class recipes, and exceptions.",
3575
- "Run decantr codify --accept after review.",
3576
- "Use decantr task <route> before LLM edits so local law appears in task context."
3577
- ]
3578
- };
3579
- const slug = input.hostedPattern.slug.trim();
3580
- const existingIds = new Set(
3581
- (patternPack.patterns ?? []).map((pattern) => typeof pattern.id === "string" ? pattern.id : "").filter(Boolean)
3582
- );
3583
- const localPatternId = existingIds.has(slug) ? `${slug}-registry-map` : slug;
3584
- const mappedPattern = {
3585
- id: localPatternId,
3586
- label: input.hostedPattern.name ? `${input.hostedPattern.name} registry mapping` : `${slug} registry mapping`,
3587
- role: "Hosted registry guidance mapped into project-owned Hybrid law",
3588
- appliesTo: [
3589
- ...input.hostedPattern.components ?? [],
3590
- ...input.hostedPattern.interactions ?? [],
3591
- ...input.hostedPattern.tags ?? []
3592
- ].slice(0, 24),
3593
- componentPaths: [],
3594
- tokenHints: [],
3595
- classHints: [],
3596
- decide: `Map hosted pattern "${slug}" into this app's existing components, tokens, classes, and exceptions before treating it as enforceable.`,
3597
- hostedPatternRefs: [input.hostedPattern],
3598
- confidence: {
3599
- tier: "low",
3600
- score: 0.25,
3601
- rationale: [
3602
- "A hosted registry pattern was selected intentionally.",
3603
- "No project-owned component path or token recipe has been mapped yet."
3604
- ]
3605
- },
3606
- enforcement: {
3607
- level: "advisory",
3608
- status: "needs-mapping",
3609
- notes: [
3610
- "This proposal does not change source files.",
3611
- "Do not enforce this hosted pattern until project-owned implementation details are filled in.",
3612
- "Use accepted local rules, ESLint, Biome, Storybook, or visual regression for deterministic blocking checks."
3613
- ]
3614
- },
3615
- evidence: [
3616
- `Hosted pattern ${input.hostedPattern.source}/${slug}${input.hostedPattern.description ? `: ${input.hostedPattern.description}` : ""}`,
3617
- input.hostedPattern.visualBrief ? `Visual brief: ${input.hostedPattern.visualBrief}` : "Visual brief not provided by the hosted pattern."
3618
- ],
3619
- evidenceToCollect: [
3620
- "Project-owned component path(s) that implement this pattern.",
3621
- "Allowed token/class recipe(s) for this app.",
3622
- "Variant names and states that are legal in this project.",
3623
- "Explicit exceptions where this hosted pattern should not apply."
3624
- ],
3625
- forbiddenAlternatives: [
3626
- "Replacing existing app components solely because a hosted pattern exists.",
3627
- "Treating hosted registry guidance as enforceable without accepted local law."
3628
- ]
3629
- };
3630
- const index = (patternPack.patterns ?? []).findIndex((pattern) => pattern.id === localPatternId);
3631
- const replacedExisting = index >= 0;
3632
- if (replacedExisting) {
3633
- patternPack.patterns[index] = mappedPattern;
3634
- } else {
3635
- patternPack.patterns = [...patternPack.patterns ?? [], mappedPattern];
3636
- }
3637
- patternPack.nextSteps = [
3638
- .../* @__PURE__ */ new Set([
3639
- ...patternPack.nextSteps ?? [],
3640
- "Fill the hosted mapping with project-owned component paths, token/class recipes, variants, and exceptions.",
3641
- "Run decantr codify --accept only after the mapping reflects the existing app.",
3642
- "Use decantr verify --brownfield --local-patterns after edits to keep mapped local law visible."
3643
- ])
3644
- ];
3645
- const patternPath = localPatternsProposalPath(input.projectRoot);
3646
- writeFileSync9(patternPath, `${JSON.stringify(patternPack, null, 2)}
3647
- `, "utf-8");
3648
- return { patternPath, localPatternId, replacedExisting };
3649
- }
3650
- function acceptBrownfieldLocalLaw(projectRoot) {
3651
- const patternProposal = readJsonFile(localPatternsProposalPath(projectRoot));
3652
- const ruleProposal = readJsonFile(localRulesProposalPath(projectRoot));
3653
- const acceptedAt = (/* @__PURE__ */ new Date()).toISOString();
3654
- let patternAcceptedPath = null;
3655
- let rulesAcceptedPath = null;
3656
- if (patternProposal) {
3657
- patternProposal.status = "accepted";
3658
- patternProposal.acceptedAt = acceptedAt;
3659
- patternAcceptedPath = localPatternsPath(projectRoot);
3660
- writeFileSync9(patternAcceptedPath, `${JSON.stringify(patternProposal, null, 2)}
3661
- `, "utf-8");
3662
- }
3663
- if (ruleProposal) {
3664
- ruleProposal.status = "accepted";
3665
- ruleProposal.acceptedAt = acceptedAt;
3666
- rulesAcceptedPath = localRulesPath(projectRoot);
3667
- writeFileSync9(rulesAcceptedPath, `${JSON.stringify(ruleProposal, null, 2)}
3668
- `, "utf-8");
3669
- }
3670
- return { patternAcceptedPath, rulesAcceptedPath };
3671
- }
3672
- function validateLocalLaw(projectRoot) {
3673
- const patternsPath = localPatternsPath(projectRoot);
3674
- const rulesPath = localRulesPath(projectRoot);
3675
- const patternPack = readJsonFile(patternsPath);
3676
- const ruleManifest = readJsonFile(rulesPath);
3677
- const warnings = [];
3678
- if (patternPack) {
3679
- const patternIds = /* @__PURE__ */ new Set();
3680
- const patterns = Array.isArray(patternPack.patterns) ? patternPack.patterns : [];
3681
- if (patterns.length === 0) {
3682
- warnings.push(".decantr/local-patterns.json has no patterns.");
3683
- }
3684
- for (const pattern of patterns) {
3685
- const id = typeof pattern.id === "string" ? pattern.id.trim() : "";
3686
- if (!id) warnings.push("A local pattern is missing an id.");
3687
- if (id && patternIds.has(id)) warnings.push(`Duplicate local pattern id: ${id}`);
3688
- if (id) patternIds.add(id);
3689
- const paths = Array.isArray(pattern.componentPaths) ? pattern.componentPaths : [];
3690
- const evidence = Array.isArray(pattern.evidence) ? pattern.evidence : [];
3691
- const todoEvidence = Array.isArray(pattern.evidenceToCollect) ? pattern.evidenceToCollect : [];
3692
- const hostedRefs = Array.isArray(pattern.hostedPatternRefs) ? pattern.hostedPatternRefs : [];
3693
- if (id && paths.length === 0 && evidence.length === 0 && todoEvidence.length > 0) {
3694
- warnings.push(
3695
- `Local pattern ${id} still reads like a TODO; add concrete component paths or evidence.`
3696
- );
3697
- }
3698
- if (id && hostedRefs.length > 0 && paths.length === 0) {
3699
- warnings.push(
3700
- `Local pattern ${id} maps hosted registry guidance but has no project-owned component path yet.`
3701
- );
3702
- }
3703
- }
3704
- }
3705
- if (ruleManifest && !Array.isArray(ruleManifest.rules)) {
3706
- warnings.push(".decantr/rules.json has no rules array.");
3707
- }
3708
- const findings = ruleManifest ? scanLocalRules(projectRoot, ruleManifest) : [];
3709
- return {
3710
- patternsPath,
3711
- rulesPath,
3712
- patternPackPresent: Boolean(patternPack),
3713
- ruleManifestPresent: Boolean(ruleManifest),
3714
- warnings,
3715
- findings
3716
- };
3717
- }
3718
- function createLocalLawTaskSummary(projectRoot) {
3719
- const patternPack = readLocalPatternPack(projectRoot);
3720
- const ruleManifest = readLocalRuleManifest(projectRoot);
3721
- const patterns = (patternPack?.patterns ?? []).map((pattern) => ({
3722
- id: typeof pattern.id === "string" ? pattern.id : "unknown",
3723
- role: typeof pattern.role === "string" ? pattern.role : null,
3724
- componentPaths: Array.isArray(pattern.componentPaths) ? pattern.componentPaths.filter((entry) => typeof entry === "string") : [],
3725
- confidenceTier: typeof pattern.confidence === "object" && pattern.confidence !== null && typeof pattern.confidence.tier === "string" ? pattern.confidence.tier : null,
3726
- enforcementLevel: typeof pattern.enforcement === "object" && pattern.enforcement !== null && typeof pattern.enforcement.level === "string" ? pattern.enforcement.level : null,
3727
- hostedPatternRefs: Array.isArray(pattern.hostedPatternRefs) ? pattern.hostedPatternRefs.map((ref) => typeof ref?.slug === "string" ? ref.slug : null).filter((slug) => Boolean(slug)) : []
3728
- }));
3729
- const rules = (ruleManifest?.rules ?? []).map((rule) => ({
3730
- id: rule.id,
3731
- severity: rule.severity,
3732
- enabled: rule.enabled,
3733
- description: rule.description
3734
- }));
3735
- return {
3736
- patternsPath: patternPack ? ".decantr/local-patterns.json" : null,
3737
- rulesPath: ruleManifest ? ".decantr/rules.json" : null,
3738
- patternCount: patterns.length,
3739
- ruleCount: rules.length,
3740
- patterns,
3741
- rules
3742
- };
3743
- }
3744
- function changedFiles(projectRoot, since) {
3745
- const changed = /* @__PURE__ */ new Set();
3746
- try {
3747
- const commands = since ? [
3748
- ["diff", "--name-only", since, "--"],
3749
- ["diff", "--name-only", "--cached"]
3750
- ] : [
3751
- ["diff", "--name-only"],
3752
- ["diff", "--name-only", "--cached"]
3753
- ];
3754
- for (const args of commands) {
3755
- const output = execFileSync("git", args, {
3756
- cwd: projectRoot,
3757
- encoding: "utf-8",
3758
- stdio: ["ignore", "pipe", "ignore"]
3759
- });
3760
- for (const line of output.split(/\r?\n/)) {
3761
- const file = line.trim();
3762
- if (file) changed.add(normalizePath(file));
3763
- }
3764
- }
3765
- } catch {
3766
- }
3767
- return [...changed].sort();
3768
- }
3769
- function routeImpacts(projectRoot, files) {
3770
- const analysis = readJsonFile(
3771
- join15(projectRoot, ".decantr", "analysis.json")
3772
- );
3773
- const routeEntries = analysis?.routes?.routes ?? [];
3774
- const impacted = /* @__PURE__ */ new Set();
3775
- for (const file of files) {
3776
- for (const route of routeEntries) {
3777
- if (route.file && pathMatches(file, route.file)) {
3778
- if (route.path) impacted.add(route.path);
3779
- }
3780
- }
3781
- }
3782
- return [...impacted].sort();
3783
- }
3784
- function scanLocalRules(projectRoot, manifest) {
3785
- const findings = [];
3786
- const files = listSourceFiles(projectRoot, 1200);
3787
- for (const rule of manifest.rules ?? []) {
3788
- if (!rule.enabled || rule.type !== "forbid-regex") continue;
3789
- const extensions = new Set(
3790
- rule.includeExtensions?.length ? rule.includeExtensions : DEFAULT_RULE_EXTENSIONS
3791
- );
3792
- let regex;
3793
- try {
3794
- regex = new RegExp(rule.pattern, "g");
3795
- } catch {
3796
- findings.push({
3797
- ruleId: rule.id,
3798
- severity: "error",
3799
- file: ".decantr/rules.json",
3800
- line: 1,
3801
- column: 1,
3802
- excerpt: rule.pattern,
3803
- message: `Invalid regex for local rule ${rule.id}.`,
3804
- suggestedFix: "Edit .decantr/rules.json so the pattern is a valid JavaScript regular expression."
3805
- });
3806
- continue;
3807
- }
3808
- let ruleFindingCount = 0;
3809
- for (const file of files) {
3810
- if (!extensions.has(extname(file.absolute))) continue;
3811
- if (pathAllowed(file.relative, rule.allowedPaths ?? [])) continue;
3812
- const contents = readFileSync11(file.absolute, "utf-8");
3813
- for (const match of contents.matchAll(regex)) {
3814
- const index = match.index ?? 0;
3815
- const position = lineColumnAt(contents, index);
3816
- findings.push({
3817
- ruleId: rule.id,
3818
- severity: rule.severity,
3819
- file: file.relative,
3820
- line: position.line,
3821
- column: position.column,
3822
- excerpt: lineAt(contents, position.line).trim().slice(0, 180),
3823
- message: rule.message,
3824
- suggestedFix: rule.suggestedFix
3825
- });
3826
- ruleFindingCount += 1;
3827
- if (rule.maxFindings && ruleFindingCount >= rule.maxFindings) break;
3828
- }
3829
- if (rule.maxFindings && ruleFindingCount >= rule.maxFindings) break;
3830
- }
3831
- }
3832
- return findings;
3833
- }
3834
- function summarizeSourceEvidence(projectRoot, files) {
3835
- const componentPaths = files.filter((file) => /(^|[/\\])components?([/\\]|$)|(^|[/\\])ui([/\\]|$)/i.test(file.relative)).map((file) => file.relative);
3836
- const byName = (terms) => componentPaths.filter((file) => terms.some((term) => basename2(file).toLowerCase().includes(term))).slice(0, 12);
3837
- const themeComponents = componentPaths.filter((file) => /theme|provider|mode|appearance|tenant|brand/i.test(file)).slice(0, 12);
3838
- const shellComponents = files.filter((file) => /layout|shell|frame|app|root|nav|sidebar/i.test(basename2(file.relative))).map((file) => file.relative).slice(0, 12);
3839
- return {
3840
- buttonComponents: byName(["button", "action"]),
3841
- cardComponents: byName(["card", "panel", "surface", "tile"]),
3842
- formComponents: byName(["input", "field", "form", "select", "textarea"]),
3843
- shellComponents,
3844
- themeComponents,
3845
- buttonVariants: detectVariants(projectRoot, files, {
3846
- family: "button",
3847
- terms: ["primary", "secondary", "tertiary", "ghost", "link", "destructive", "icon"]
3848
- }),
3849
- cardVariants: detectVariants(projectRoot, files, {
3850
- family: "surface",
3851
- terms: ["card", "panel", "surface", "tile", "elevated", "interactive", "glass"]
3852
- }),
3853
- formVariants: detectVariants(projectRoot, files, {
3854
- family: "form",
3855
- terms: ["error", "invalid", "disabled", "required", "success", "helper"]
3856
- }),
3857
- themeVariants: detectVariants(projectRoot, files, {
3858
- family: "theme",
3859
- terms: ["dark", "light", "brand", "tenant", "theme", "mode", "density"]
3860
- }),
3861
- buttonSourceEvidence: collectSourceEvidence(projectRoot, files, [
3862
- "button",
3863
- "primary",
3864
- "secondary",
3865
- "tertiary",
3866
- "destructive"
3867
- ]),
3868
- cardSourceEvidence: collectSourceEvidence(projectRoot, files, [
3869
- "card",
3870
- "panel",
3871
- "surface",
3872
- "rounded",
3873
- "shadow"
3874
- ]),
3875
- formSourceEvidence: collectSourceEvidence(projectRoot, files, [
3876
- "input",
3877
- "label",
3878
- "error",
3879
- "field",
3880
- "select"
3881
- ]),
3882
- themeSourceEvidence: collectSourceEvidence(projectRoot, files, [
3883
- "theme",
3884
- "dark",
3885
- "light",
3886
- "brand",
3887
- "tenant"
3888
- ]),
3889
- buttonClassHints: collectClassHints(projectRoot, files, [
3890
- "button",
3891
- "btn",
3892
- "action",
3893
- "primary",
3894
- "secondary",
3895
- "tertiary",
3896
- "ghost",
3897
- "link"
3898
- ]),
3899
- cardClassHints: collectClassHints(projectRoot, files, ["card", "panel", "surface", "tile"])
3900
- };
3901
- }
3902
- function detectVariants(projectRoot, files, input) {
3903
- const variants = /* @__PURE__ */ new Map();
3904
- const termPattern = new RegExp(
3905
- `\\b(${input.terms.map(escapeRegExp).join("|")})(?:[-_:][a-z0-9-]+)?\\b`,
3906
- "gi"
3907
- );
3908
- for (const file of files) {
3909
- if (!UI_TEMPLATE_EXTENSIONS.has(extname(file.absolute))) continue;
3910
- const content = readFileSync11(join15(projectRoot, file.relative), "utf-8");
3911
- for (const match of content.matchAll(termPattern)) {
3912
- const raw = (match[0] ?? "").toLowerCase();
3913
- const id = raw.replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
3914
- if (!id || id.length < 3) continue;
3915
- const position = lineColumnAt(content, match.index ?? 0);
3916
- const line = lineAt(content, position.line).trim().slice(0, 160);
3917
- const entry = variants.get(id) ?? { evidence: /* @__PURE__ */ new Set(), count: 0 };
3918
- entry.count += 1;
3919
- entry.evidence.add(`${file.relative}:${position.line} ${line}`);
3920
- variants.set(id, entry);
3921
- }
3922
- }
3923
- return [...variants.entries()].sort((a, b) => b[1].count - a[1].count || a[0].localeCompare(b[0])).slice(0, 8).map(([id, value]) => ({
3924
- id,
3925
- label: `${input.family} ${id}`,
3926
- evidence: [...value.evidence].slice(0, 4),
3927
- confidence: value.count >= 4 ? "high" : value.count >= 2 ? "medium" : "low"
3928
- }));
3929
- }
3930
- function collectSourceEvidence(projectRoot, files, terms) {
3931
- const results = [];
3932
- const termPattern = new RegExp(terms.map(escapeRegExp).join("|"), "i");
3933
- for (const file of files) {
3934
- if (!UI_TEMPLATE_EXTENSIONS.has(extname(file.absolute))) continue;
3935
- const content = readFileSync11(join15(projectRoot, file.relative), "utf-8");
3936
- const lines = content.split(/\r?\n/);
3937
- for (let index = 0; index < lines.length; index += 1) {
3938
- const line = lines[index];
3939
- if (!termPattern.test(line)) continue;
3940
- const signals = terms.filter((term) => line.toLowerCase().includes(term.toLowerCase()));
3941
- results.push({
3942
- file: file.relative,
3943
- line: index + 1,
3944
- excerpt: line.trim().slice(0, 180),
3945
- signals: signals.slice(0, 6)
3946
- });
3947
- if (results.length >= 16) return results;
3948
- break;
3949
- }
3950
- }
3951
- return results;
3952
- }
3953
- function confidenceForEvidence(input) {
3954
- const score = Math.min(
3955
- 1,
3956
- input.componentCount * 0.28 + (input.classHintCount ?? 0) * 0.08 + (input.variantCount ?? 0) * 0.08 + (input.sourceEvidenceCount ?? 0) * 0.05
3957
- );
3958
- const tier = score >= 0.7 ? "high" : score >= 0.4 ? "medium" : "low";
3959
- const rationale = [
3960
- input.componentCount > 0 ? `${input.componentCount} likely component path(s) detected` : "no likely component wrapper detected yet",
3961
- (input.classHintCount ?? 0) > 0 ? `${input.classHintCount} class recipe hint(s) detected` : "no strong class recipe hints detected yet",
3962
- (input.variantCount ?? 0) > 0 ? `${input.variantCount} possible variant signal(s) detected` : "variant names still need team confirmation"
3963
- ];
3964
- return { tier, score: Number(score.toFixed(2)), rationale };
3965
- }
3966
- function collectClassHints(projectRoot, files, terms) {
3967
- const hints = /* @__PURE__ */ new Map();
3968
- const wantsButton = terms.some(
3969
- (term) => /^(button|btn|action|primary|secondary|tertiary|ghost|link)$/i.test(term)
3970
- );
3971
- const wantsSurface = terms.some((term) => /^(card|panel|surface|tile)$/i.test(term));
3972
- const buttonSignal = /button|btn|action|primary|secondary|tertiary|ghost|link|destructive|icon/i;
3973
- const surfaceSignal = /card|panel|surface|tile|rounded|shadow|border|bg-|p-\d|px-|py-/i;
3974
- for (const file of files) {
3975
- if (!UI_TEMPLATE_EXTENSIONS.has(extname(file.absolute))) continue;
3976
- const content = readFileSync11(join15(projectRoot, file.relative), "utf-8");
3977
- if (!terms.some((term) => content.toLowerCase().includes(term))) continue;
3978
- if (wantsButton) {
3979
- const openingTags = content.matchAll(
3980
- /<([A-Za-z][\w.:/-]*)\b[^>]*\bclass(?:Name)?\s*=\s*["'`]([^"'`]+)["'`][^>]*>/g
3981
- );
3982
- for (const match of openingTags) {
3983
- const tag = match[1];
3984
- const value = match[2].trim();
3985
- const tagLooksInteractive = /^(button|a|Link)$/i.test(tag) || /(^|\.)(Button|IconButton|LinkButton|Action)$/i.test(tag);
3986
- const fileLooksInteractive = /button|action|link/i.test(basename2(file.relative));
3987
- if (!tagLooksInteractive && !fileLooksInteractive) continue;
3988
- if (!buttonSignal.test(value) && !fileLooksInteractive) continue;
3989
- hints.set(value, (hints.get(value) ?? 0) + 1);
3990
- }
3991
- continue;
3992
- }
3993
- const matches = content.matchAll(/\bclass(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/g);
3994
- for (const match of matches) {
3995
- const value = match[1].trim();
3996
- const keep = wantsSurface && surfaceSignal.test(value) || !wantsButton && !wantsSurface && (buttonSignal.test(value) || surfaceSignal.test(value));
3997
- if (!keep) continue;
3998
- hints.set(value, (hints.get(value) ?? 0) + 1);
3999
- }
4000
- }
4001
- return [...hints.entries()].sort((a, b) => b[1] - a[1]).slice(0, 8).map(([hint]) => hint);
4002
- }
4003
- function listSourceFiles(projectRoot, maxFiles) {
4004
- const files = [];
4005
- const visit = (dir) => {
4006
- if (files.length >= maxFiles) return;
4007
- let entries;
4008
- try {
4009
- entries = readdirSync5(dir);
4010
- } catch {
4011
- return;
4012
- }
4013
- for (const entry of entries) {
4014
- if (files.length >= maxFiles) return;
4015
- if (IGNORED_DIRS.has(entry)) continue;
4016
- const absolute = join15(dir, entry);
4017
- let stat;
4018
- try {
4019
- stat = statSync5(absolute);
4020
- } catch {
4021
- continue;
4022
- }
4023
- if (stat.isDirectory()) {
4024
- visit(absolute);
4025
- } else if (stat.isFile() && SOURCE_EXTENSIONS.has(extname(entry))) {
4026
- files.push({ absolute, relative: normalizePath(relative3(projectRoot, absolute)) });
4027
- }
4028
- }
4029
- };
4030
- visit(projectRoot);
4031
- return files.sort((a, b) => a.relative.localeCompare(b.relative));
4032
- }
4033
- function readJsonFile(path) {
4034
- if (!existsSync13(path)) return null;
4035
- try {
4036
- return JSON.parse(readFileSync11(path, "utf-8"));
4037
- } catch {
4038
- return null;
4039
- }
4040
- }
4041
- function pathAllowed(file, allowedPaths) {
4042
- return allowedPaths.some((allowedPath) => pathMatches(file, allowedPath));
4043
- }
4044
- function pathMatches(file, pattern) {
4045
- const normalizedFile = normalizePath(file);
4046
- const normalizedPattern = normalizePath(pattern);
4047
- return normalizedFile === normalizedPattern || normalizedFile.endsWith(`/${normalizedPattern}`);
4048
- }
4049
- function normalizePath(path) {
4050
- return path.split(sep).join("/").replace(/\\/g, "/");
4051
- }
4052
- function lineColumnAt(contents, index) {
4053
- const before = contents.slice(0, index);
4054
- const lines = before.split(/\r?\n/);
4055
- return {
4056
- line: lines.length,
4057
- column: lines[lines.length - 1].length + 1
4058
- };
4059
- }
4060
- function escapeRegExp(value) {
4061
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4062
- }
4063
- function lineAt(contents, line) {
4064
- return contents.split(/\r?\n/)[line - 1] ?? "";
4065
- }
4066
-
4067
- // src/style-bridge.ts
4068
- import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
4069
- import { join as join16 } from "path";
4070
- function styleBridgeProposalPath(projectRoot) {
4071
- return join16(projectRoot, ".decantr", "style-bridge.proposal.json");
4072
- }
4073
- function styleBridgePath(projectRoot) {
4074
- return join16(projectRoot, ".decantr", "style-bridge.json");
4075
- }
4076
- function readJsonFile2(path) {
4077
- if (!existsSync14(path)) return null;
4078
- try {
4079
- return JSON.parse(readFileSync12(path, "utf-8"));
4080
- } catch {
4081
- return null;
4082
- }
4083
- }
4084
- function writeJsonFile(path, value) {
4085
- writeFileSync10(path, `${JSON.stringify(value, null, 2)}
4086
- `, "utf-8");
4087
- }
4088
- function readStyleBridge(projectRoot) {
4089
- return readJsonFile2(styleBridgePath(projectRoot));
4090
- }
4091
- function readStyleBridgeProposal(projectRoot) {
4092
- return readJsonFile2(styleBridgeProposalPath(projectRoot));
4093
- }
4094
- function stringArray(value) {
4095
- return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
4096
- }
4097
- function readThemeInventory(projectRoot) {
4098
- const inventory = readJsonFile2(join16(projectRoot, ".decantr", "theme-inventory.json"));
4099
- return {
4100
- modes: stringArray(inventory?.modes),
4101
- variantIds: Array.isArray(inventory?.variants) ? inventory.variants.map((variant) => variant.id).filter((id) => typeof id === "string") : [],
4102
- darkModeDetected: typeof inventory?.darkModeDetected === "boolean" ? inventory.darkModeDetected : null
4103
- };
4104
- }
4105
- function tokenHints(styling, terms) {
4106
- return styling.cssVariables.filter((name) => terms.test(name)).slice(0, 12);
4107
- }
4108
- function readProjectPatternPack(projectRoot) {
4109
- return readLocalPatternPack(projectRoot) ?? readJsonFile2(
4110
- join16(projectRoot, ".decantr", "local-patterns.proposal.json")
4111
- );
4112
- }
4113
- function classHintsForPattern(projectRoot, ids) {
4114
- const pack = readProjectPatternPack(projectRoot);
4115
- const classes = /* @__PURE__ */ new Set();
4116
- for (const pattern of pack?.patterns ?? []) {
4117
- if (!ids.includes(String(pattern.id))) continue;
4118
- for (const hint of pattern.classHints ?? []) classes.add(hint);
4119
- }
4120
- return [...classes].slice(0, 12);
4121
- }
4122
- function sourceEvidence(projectRoot, ids) {
4123
- const pack = readProjectPatternPack(projectRoot);
4124
- const evidence = /* @__PURE__ */ new Set();
4125
- for (const pattern of pack?.patterns ?? []) {
4126
- if (!ids.includes(String(pattern.id))) continue;
4127
- for (const path of pattern.componentPaths ?? []) evidence.add(path);
4128
- for (const item of pattern.evidence ?? []) evidence.add(item);
4129
- }
4130
- return [...evidence].slice(0, 12);
4131
- }
4132
- function colorTokenNames(styling) {
4133
- const names = new Set(Object.keys(styling.colors ?? {}));
4134
- for (const variable of styling.cssVariables) {
4135
- if (/color|bg|background|surface|text|foreground|border|primary|secondary|accent|muted/i.test(variable)) {
4136
- names.add(variable);
4137
- }
4138
- }
4139
- return [...names].slice(0, 40);
4140
- }
4141
- function createStyleBridgeProposal(input) {
4142
- const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
4143
- const theme = readThemeInventory(input.projectRoot);
4144
- const routeCount = input.essence && typeof input.essence === "object" && "blueprint" in input.essence ? Object.keys(input.essence.blueprint?.routes ?? {}).length : 0;
4145
- const target = input.essence && typeof input.essence === "object" && "meta" in input.essence ? String(input.essence.meta?.target ?? "") || null : null;
4146
- const darkModeDetected = theme.darkModeDetected ?? input.styling.darkMode;
4147
- const themeModes = theme.modes.length > 0 ? theme.modes : darkModeDetected ? ["base", "dark"] : ["base"];
4148
- const themeVariantIds = theme.variantIds.length > 0 ? theme.variantIds : themeModes.filter((mode) => mode !== "base");
4149
- return {
4150
- version: 1,
4151
- status: "proposal",
4152
- generatedAt,
4153
- source: "decantr codify --style-bridge",
4154
- purpose: "Project-owned Hybrid style bridge. It maps Decantr design intent to the existing app styling system without installing Decantr CSS or taking over source.",
4155
- adoption: {
4156
- mode: "style-bridge",
4157
- workflowMode: "brownfield-attach",
4158
- sourceAuthority: "Existing production source stays authoritative.",
4159
- styleAuthority: "Use these mappings to translate Decantr concepts into project-owned tokens, classes, and components.",
4160
- notRuntimeTakeover: true,
4161
- authorityPrecedence: [
4162
- "existing production source",
4163
- "accepted style bridge",
4164
- "accepted local patterns and rules",
4165
- "Essence V4 contract",
4166
- "hosted registry patterns and execution packs as optional guidance"
4167
- ]
4168
- },
4169
- project: {
4170
- framework: input.detected.framework,
4171
- packageManager: input.detected.packageManager,
4172
- target,
4173
- routeCount
4174
- },
4175
- styling: {
4176
- approach: input.styling.approach,
4177
- configFile: input.styling.configFile ?? null,
4178
- darkModeDetected,
4179
- cssVariables: input.styling.cssVariables.slice(0, 80),
4180
- colorTokenNames: colorTokenNames(input.styling),
4181
- themeModes,
4182
- themeVariantIds
4183
- },
4184
- mappings: [
4185
- {
4186
- id: "surface",
4187
- label: "Surfaces and cards",
4188
- decantrIntent: "surface background, card treatment, border, radius, depth, and hover state",
4189
- projectAuthority: "Use the app card/surface primitives, tokens, and accepted local law.",
4190
- tokenHints: tokenHints(input.styling, /surface|card|panel|bg|background|border|shadow|radius/i),
4191
- classHints: classHintsForPattern(input.projectRoot, ["surface-card"]),
4192
- sourceEvidence: sourceEvidence(input.projectRoot, ["surface-card"]),
4193
- guardrails: [
4194
- "Do not invent a new card color/radius/shadow recipe without updating local law.",
4195
- "Do not add Decantr CSS d-* classes unless adoption mode changes to decantr-css."
4196
- ]
4197
- },
4198
- {
4199
- id: "action",
4200
- label: "Actions and buttons",
4201
- decantrIntent: "primary, secondary, tertiary, destructive, icon-only, loading, and disabled action states",
4202
- projectAuthority: "Use the app button/action primitives and local variant names.",
4203
- tokenHints: tokenHints(input.styling, /primary|secondary|accent|danger|error|destructive|focus/i),
4204
- classHints: classHintsForPattern(input.projectRoot, ["button"]),
4205
- sourceEvidence: sourceEvidence(input.projectRoot, ["button"]),
4206
- guardrails: [
4207
- "Map any new action style to primary/secondary/tertiary/destructive/icon before coding.",
4208
- "Avoid raw button markup when a project-owned primitive exists."
4209
- ]
4210
- },
4211
- {
4212
- id: "focus-accessibility",
4213
- label: "Focus and accessibility",
4214
- decantrIntent: "visible focus, keyboard clarity, contrast, and reduced-motion safety",
4215
- projectAuthority: "Use the app accessibility classes, focus tokens, and framework conventions.",
4216
- tokenHints: tokenHints(input.styling, /focus|ring|outline|contrast|motion|duration/i),
4217
- classHints: [],
4218
- sourceEvidence: [],
4219
- guardrails: [
4220
- "Every new interactive control needs visible focus evidence.",
4221
- "Motion should honor prefers-reduced-motion or an existing app motion helper."
4222
- ]
4223
- },
4224
- {
4225
- id: "layout-density",
4226
- label: "Layout density and spacing",
4227
- decantrIntent: "route gutters, section gaps, component padding, density, and responsive rhythm",
4228
- projectAuthority: "Use existing layout wrappers, shell primitives, and spacing tokens/classes.",
4229
- tokenHints: tokenHints(input.styling, /space|spacing|gap|gutter|container|radius/i),
4230
- classHints: classHintsForPattern(input.projectRoot, ["page-shell"]),
4231
- sourceEvidence: sourceEvidence(input.projectRoot, ["page-shell"]),
4232
- guardrails: [
4233
- "Do not let each page invent independent max-width, gutters, or scroll ownership.",
4234
- "Use the detected shell/layout authority before adding a new wrapper."
4235
- ]
4236
- },
4237
- {
4238
- id: "theme-variant",
4239
- label: "Theme variants",
4240
- decantrIntent: "light, dark, brand, tenant, seasonal, and density variants",
4241
- projectAuthority: "Use the app theme provider, data attributes, CSS variables, or Tailwind mode strategy.",
4242
- tokenHints: tokenHints(input.styling, /theme|dark|light|brand|tenant|mode|color/i),
4243
- classHints: classHintsForPattern(input.projectRoot, ["theme-variant"]),
4244
- sourceEvidence: sourceEvidence(input.projectRoot, ["theme-variant"]),
4245
- guardrails: [
4246
- "Keep theme modes documented in .decantr/theme-inventory.json and this bridge.",
4247
- "Do not treat theme switcher container classes as standalone theme variants."
4248
- ]
4249
- }
4250
- ],
4251
- rules: [
4252
- "The bridge is advisory until accepted; after acceptance, task/doctor/CI should surface it as Hybrid authority.",
4253
- "The bridge does not make hosted registry patterns enforceable. Map hosted concepts into local law first.",
4254
- "Keep deterministic blocking checks in .decantr/rules.json, ESLint, Biome, tests, or visual regression."
4255
- ],
4256
- nextSteps: [
4257
- "Review token and class hints. Replace generic hints with the exact project tokens/classes the team owns.",
4258
- "Run decantr codify --accept to promote the proposal to .decantr/style-bridge.json.",
4259
- "Use decantr task <route> before LLM edits so the style bridge appears in task context.",
4260
- "Run decantr ci or decantr verify after edits to keep local law and Project Health visible."
4261
- ]
4262
- };
4263
- }
4264
- function writeStyleBridgeProposal(projectRoot, proposal) {
4265
- const decantrDir = join16(projectRoot, ".decantr");
4266
- mkdirSync9(decantrDir, { recursive: true });
4267
- const path = styleBridgeProposalPath(projectRoot);
4268
- writeJsonFile(path, proposal);
4269
- return path;
4270
- }
4271
- function acceptStyleBridge(projectRoot) {
4272
- const proposal = readStyleBridgeProposal(projectRoot);
4273
- if (!proposal) return null;
4274
- const accepted = {
4275
- ...proposal,
4276
- status: "accepted",
4277
- acceptedAt: (/* @__PURE__ */ new Date()).toISOString()
4278
- };
4279
- const path = styleBridgePath(projectRoot);
4280
- writeJsonFile(path, accepted);
4281
- const projectJsonPath = join16(projectRoot, ".decantr", "project.json");
4282
- const projectJson = readJsonFile2(projectJsonPath) ?? {};
4283
- const initialized = typeof projectJson.initialized === "object" && projectJson.initialized !== null ? projectJson.initialized : {};
4284
- writeJsonFile(projectJsonPath, {
4285
- ...projectJson,
4286
- initialized: {
4287
- ...initialized,
4288
- workflowMode: initialized.workflowMode ?? "brownfield-attach",
4289
- adoptionMode: "style-bridge"
4290
- }
4291
- });
4292
- return path;
4293
- }
4294
- function createStyleBridgeTaskSummary(projectRoot) {
4295
- const bridge = readStyleBridge(projectRoot);
4296
- return {
4297
- path: bridge ? ".decantr/style-bridge.json" : null,
4298
- status: bridge?.status ?? null,
4299
- mappingCount: bridge?.mappings?.length ?? 0,
4300
- stylingApproach: bridge?.styling?.approach ?? null,
4301
- themeModes: bridge?.styling?.themeModes ?? [],
4302
- mappings: bridge?.mappings?.map((mapping) => ({
4303
- id: mapping.id,
4304
- label: mapping.label,
4305
- tokenHints: mapping.tokenHints.slice(0, 6),
4306
- classHints: mapping.classHints.slice(0, 4),
4307
- guardrails: mapping.guardrails.slice(0, 3)
4308
- })) ?? []
4309
- };
4310
- }
4311
- function styleBridgeMatches(projectRoot, query) {
4312
- if (!projectRoot) return [];
4313
- const bridge = readStyleBridge(projectRoot);
4314
- if (!bridge) return [];
4315
- const terms = query.toLowerCase().split(/[^a-z0-9]+/).filter((term) => term.length > 1).flatMap((term) => term.endsWith("s") && term.length > 3 ? [term, term.slice(0, -1)] : [term]);
4316
- if (terms.length === 0) return [];
4317
- return bridge.mappings.map((mapping) => {
4318
- const haystack = [
4319
- mapping.id,
4320
- mapping.label,
4321
- mapping.decantrIntent,
4322
- mapping.projectAuthority,
4323
- ...mapping.tokenHints,
4324
- ...mapping.classHints,
4325
- ...mapping.guardrails
4326
- ].join(" ").toLowerCase();
4327
- const score = terms.reduce((sum, term) => sum + (haystack.includes(term) ? 1 : 0), 0);
4328
- return {
4329
- id: mapping.id,
4330
- label: mapping.label,
4331
- score,
4332
- tokenHints: mapping.tokenHints.slice(0, 4),
4333
- classHints: mapping.classHints.slice(0, 3)
4334
- };
4335
- }).filter((match) => match.score > 0).sort((a, b) => b.score - a.score || a.id.localeCompare(b.id)).slice(0, 5);
4336
- }
4337
-
4338
- // src/commands/ci.ts
3358
+ import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
3359
+ import { dirname as dirname3, join as join15, relative as relative3, resolve } from "path";
4339
3360
  var BOLD2 = "\x1B[1m";
4340
3361
  var DIM3 = "\x1B[2m";
4341
3362
  var GREEN3 = "\x1B[32m";
@@ -4343,9 +3364,9 @@ var RED2 = "\x1B[31m";
4343
3364
  var RESET3 = "\x1B[0m";
4344
3365
  var CI_SCHEMA = "https://decantr.ai/schemas/decantr-ci-report.v1.json";
4345
3366
  function readJson(path) {
4346
- if (!existsSync15(path)) return null;
3367
+ if (!existsSync13(path)) return null;
4347
3368
  try {
4348
- return JSON.parse(readFileSync13(path, "utf-8"));
3369
+ return JSON.parse(readFileSync11(path, "utf-8"));
4349
3370
  } catch {
4350
3371
  return null;
4351
3372
  }
@@ -4389,21 +3410,21 @@ function parseProvider(value) {
4389
3410
  throw new Error("Invalid --provider value. Use github or generic.");
4390
3411
  }
4391
3412
  function detectPackageManager2(root) {
4392
- const pkg = readJson(join17(root, "package.json"));
3413
+ const pkg = readJson(join15(root, "package.json"));
4393
3414
  const declared = pkg?.packageManager?.split("@")[0];
4394
3415
  if (declared === "pnpm" || declared === "npm" || declared === "yarn" || declared === "bun") {
4395
3416
  return declared;
4396
3417
  }
4397
- if (existsSync15(join17(root, "pnpm-lock.yaml"))) return "pnpm";
4398
- if (existsSync15(join17(root, "package-lock.json"))) return "npm";
4399
- if (existsSync15(join17(root, "yarn.lock"))) return "yarn";
4400
- if (existsSync15(join17(root, "bun.lock")) || existsSync15(join17(root, "bun.lockb"))) return "bun";
3418
+ if (existsSync13(join15(root, "pnpm-lock.yaml"))) return "pnpm";
3419
+ if (existsSync13(join15(root, "package-lock.json"))) return "npm";
3420
+ if (existsSync13(join15(root, "yarn.lock"))) return "yarn";
3421
+ if (existsSync13(join15(root, "bun.lock")) || existsSync13(join15(root, "bun.lockb"))) return "bun";
4401
3422
  return "unknown";
4402
3423
  }
4403
3424
  function hasWorkspaceMarker(root) {
4404
- const pkg = readJson(join17(root, "package.json"));
3425
+ const pkg = readJson(join15(root, "package.json"));
4405
3426
  return Boolean(
4406
- existsSync15(join17(root, "pnpm-workspace.yaml")) || existsSync15(join17(root, "turbo.json")) || existsSync15(join17(root, "nx.json")) || pkg?.workspaces
3427
+ existsSync13(join15(root, "pnpm-workspace.yaml")) || existsSync13(join15(root, "turbo.json")) || existsSync13(join15(root, "nx.json")) || pkg?.workspaces
4407
3428
  );
4408
3429
  }
4409
3430
  function installCommand(packageManager) {
@@ -4449,7 +3470,7 @@ function decantrCommand(packageManager) {
4449
3470
  }
4450
3471
  }
4451
3472
  function localCliPinned(root) {
4452
- const pkg = readJson(join17(root, "package.json"));
3473
+ const pkg = readJson(join15(root, "package.json"));
4453
3474
  return Boolean(pkg?.devDependencies?.["@decantr/cli"] || pkg?.dependencies?.["@decantr/cli"]);
4454
3475
  }
4455
3476
  function projectSlug(projectPath) {
@@ -4457,8 +3478,8 @@ function projectSlug(projectPath) {
4457
3478
  }
4458
3479
  function writeOutput(root, path, content) {
4459
3480
  const absolute = resolve(root, path);
4460
- mkdirSync10(dirname3(absolute), { recursive: true });
4461
- writeFileSync11(absolute, content, "utf-8");
3481
+ mkdirSync8(dirname3(absolute), { recursive: true });
3482
+ writeFileSync9(absolute, content, "utf-8");
4462
3483
  }
4463
3484
  function summarizeLocalLaw(projectRoot) {
4464
3485
  const validation = validateLocalLaw(projectRoot);
@@ -4733,7 +3754,7 @@ jobs:
4733
3754
  }
4734
3755
  function writeCiInit(root, options) {
4735
3756
  const workspaceInfo = resolveWorkspaceInfo(root, options.project);
4736
- if (options.project && !existsSync15(workspaceInfo.appRoot)) {
3757
+ if (options.project && !existsSync13(workspaceInfo.appRoot)) {
4737
3758
  throw new Error(`Project path does not exist: ${options.project}`);
4738
3759
  }
4739
3760
  if (workspaceInfo.requiresProjectSelection && !options.workspace) {
@@ -4743,7 +3764,7 @@ function writeCiInit(root, options) {
4743
3764
  );
4744
3765
  }
4745
3766
  const outputRoot = workspaceInfo.workspaceRoot;
4746
- const projectPath = options.project ?? (options.workspace || workspaceInfo.appRoot === workspaceInfo.workspaceRoot ? void 0 : relative4(workspaceInfo.workspaceRoot, workspaceInfo.appRoot).replace(/\\/g, "/"));
3767
+ const projectPath = options.project ?? (options.workspace || workspaceInfo.appRoot === workspaceInfo.workspaceRoot ? void 0 : relative3(workspaceInfo.workspaceRoot, workspaceInfo.appRoot).replace(/\\/g, "/"));
4747
3768
  const packageManager = detectPackageManager2(outputRoot);
4748
3769
  const command = decantrCommand(packageManager);
4749
3770
  const failOn = options.failOn ?? "error";
@@ -4756,7 +3777,7 @@ function writeCiInit(root, options) {
4756
3777
  if (provider === "generic") {
4757
3778
  const path2 = ".decantr/ci/decantr-ci.sh";
4758
3779
  const absolute2 = resolve(outputRoot, path2);
4759
- if (existsSync15(absolute2) && !options.force) {
3780
+ if (existsSync13(absolute2) && !options.force) {
4760
3781
  throw new Error(`${path2} already exists. Re-run with --force to replace it.`);
4761
3782
  }
4762
3783
  writeOutput(
@@ -4777,7 +3798,7 @@ function writeCiInit(root, options) {
4777
3798
  }
4778
3799
  const path = ".github/workflows/decantr-ci.yml";
4779
3800
  const absolute = resolve(outputRoot, path);
4780
- if (existsSync15(absolute) && !options.force) {
3801
+ if (existsSync13(absolute) && !options.force) {
4781
3802
  throw new Error(`${path} already exists. Re-run with --force to replace it.`);
4782
3803
  }
4783
3804
  writeOutput(
@@ -4826,7 +3847,7 @@ async function runWorkspaceCi(root, options) {
4826
3847
  }
4827
3848
  async function runProjectCi(root, options) {
4828
3849
  const workspaceInfo = resolveWorkspaceInfo(root, options.project);
4829
- if (options.project && !existsSync15(workspaceInfo.appRoot)) {
3850
+ if (options.project && !existsSync13(workspaceInfo.appRoot)) {
4830
3851
  console.error(`${RED2}Project path does not exist: ${options.project}${RESET3}`);
4831
3852
  return 1;
4832
3853
  }
@@ -4900,8 +3921,8 @@ async function cmdCi(args = ["ci"], root = process.cwd()) {
4900
3921
  }
4901
3922
 
4902
3923
  // src/commands/create.ts
4903
- import { existsSync as existsSync16, mkdirSync as mkdirSync11, writeFileSync as writeFileSync12 } from "fs";
4904
- import { join as join18 } from "path";
3924
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9, writeFileSync as writeFileSync10 } from "fs";
3925
+ import { join as join16 } from "path";
4905
3926
  import {
4906
3927
  CONTENT_TYPE_TO_API_CONTENT_TYPE,
4907
3928
  CONTENT_TYPES
@@ -5097,25 +4118,25 @@ function cmdCreate(type, name, projectRoot = process.cwd()) {
5097
4118
  }
5098
4119
  const contentType = type;
5099
4120
  const plural = PLURAL[contentType];
5100
- const customDir = join18(projectRoot, ".decantr", "custom", plural);
5101
- const filePath = join18(customDir, `${name}.json`);
5102
- if (existsSync16(filePath)) {
4121
+ const customDir = join16(projectRoot, ".decantr", "custom", plural);
4122
+ const filePath = join16(customDir, `${name}.json`);
4123
+ if (existsSync14(filePath)) {
5103
4124
  console.error(`${type} "${name}" already exists at ${filePath}`);
5104
4125
  process.exitCode = 1;
5105
4126
  return;
5106
4127
  }
5107
- mkdirSync11(customDir, { recursive: true });
4128
+ mkdirSync9(customDir, { recursive: true });
5108
4129
  const skeleton = getSkeleton(contentType, name, humanizeId(name));
5109
- writeFileSync12(filePath, JSON.stringify(skeleton, null, 2));
4130
+ writeFileSync10(filePath, JSON.stringify(skeleton, null, 2));
5110
4131
  console.log(`Created ${type} "${name}" at ${filePath}`);
5111
4132
  console.log(`Edit it, then publish with: decantr publish ${type} ${name}`);
5112
4133
  }
5113
4134
 
5114
4135
  // src/commands/doctor.ts
5115
- import { existsSync as existsSync17, readdirSync as readdirSync6, readFileSync as readFileSync14 } from "fs";
5116
- import { dirname as dirname4, join as join19, relative as relative5 } from "path";
4136
+ import { existsSync as existsSync15, readdirSync as readdirSync5, readFileSync as readFileSync12 } from "fs";
4137
+ import { dirname as dirname4, join as join17, relative as relative4 } from "path";
5117
4138
  import { fileURLToPath } from "url";
5118
- import { isV4 as isV44 } from "@decantr/essence-spec";
4139
+ import { isV4 as isV43 } from "@decantr/essence-spec";
5119
4140
  import { collectMissingPackManifestFiles } from "@decantr/verifier";
5120
4141
  var BOLD3 = "\x1B[1m";
5121
4142
  var DIM4 = "\x1B[2m";
@@ -5125,25 +4146,25 @@ var YELLOW3 = "\x1B[33m";
5125
4146
  var CYAN2 = "\x1B[36m";
5126
4147
  var RESET4 = "\x1B[0m";
5127
4148
  function readJson2(path) {
5128
- if (!existsSync17(path)) return null;
4149
+ if (!existsSync15(path)) return null;
5129
4150
  try {
5130
- return JSON.parse(readFileSync14(path, "utf-8"));
4151
+ return JSON.parse(readFileSync12(path, "utf-8"));
5131
4152
  } catch {
5132
4153
  return null;
5133
4154
  }
5134
4155
  }
5135
4156
  function isContractOnlyProject(projectRoot) {
5136
- const projectJson = readJson2(join19(projectRoot, ".decantr", "project.json"));
4157
+ const projectJson = readJson2(join17(projectRoot, ".decantr", "project.json"));
5137
4158
  return projectJson?.initialized?.adoptionMode === "contract-only" || projectJson?.initialized?.adoptionMode === "style-bridge";
5138
4159
  }
5139
4160
  function readCliPackageVersion() {
5140
- const packagePath = join19(dirname4(fileURLToPath(import.meta.url)), "..", "..", "package.json");
5141
- const srcPackagePath = join19(dirname4(fileURLToPath(import.meta.url)), "..", "package.json");
4161
+ const packagePath = join17(dirname4(fileURLToPath(import.meta.url)), "..", "..", "package.json");
4162
+ const srcPackagePath = join17(dirname4(fileURLToPath(import.meta.url)), "..", "package.json");
5142
4163
  const pkg = readJson2(packagePath) ?? readJson2(srcPackagePath);
5143
4164
  return pkg?.version ?? "unknown";
5144
4165
  }
5145
4166
  function readPackageJson2(dir) {
5146
- return readJson2(join19(dir, "package.json"));
4167
+ return readJson2(join17(dir, "package.json"));
5147
4168
  }
5148
4169
  function detectPackageManager3(root) {
5149
4170
  const pkg = readPackageJson2(root);
@@ -5151,10 +4172,10 @@ function detectPackageManager3(root) {
5151
4172
  if (declared === "pnpm" || declared === "npm" || declared === "yarn" || declared === "bun") {
5152
4173
  return declared;
5153
4174
  }
5154
- if (existsSync17(join19(root, "pnpm-lock.yaml"))) return "pnpm";
5155
- if (existsSync17(join19(root, "package-lock.json"))) return "npm";
5156
- if (existsSync17(join19(root, "yarn.lock"))) return "yarn";
5157
- if (existsSync17(join19(root, "bun.lock")) || existsSync17(join19(root, "bun.lockb"))) return "bun";
4175
+ if (existsSync15(join17(root, "pnpm-lock.yaml"))) return "pnpm";
4176
+ if (existsSync15(join17(root, "package-lock.json"))) return "npm";
4177
+ if (existsSync15(join17(root, "yarn.lock"))) return "yarn";
4178
+ if (existsSync15(join17(root, "bun.lock")) || existsSync15(join17(root, "bun.lockb"))) return "bun";
5158
4179
  return "unknown";
5159
4180
  }
5160
4181
  function localCliDependency(root) {
@@ -5164,7 +4185,7 @@ function localCliDependency(root) {
5164
4185
  function hasWorkspaceMarker2(root) {
5165
4186
  const pkg = readPackageJson2(root);
5166
4187
  return Boolean(
5167
- existsSync17(join19(root, "pnpm-workspace.yaml")) || existsSync17(join19(root, "turbo.json")) || existsSync17(join19(root, "nx.json")) || pkg?.workspaces
4188
+ existsSync15(join17(root, "pnpm-workspace.yaml")) || existsSync15(join17(root, "turbo.json")) || existsSync15(join17(root, "nx.json")) || pkg?.workspaces
5168
4189
  );
5169
4190
  }
5170
4191
  function pinCliCommand2(packageManager, root) {
@@ -5182,13 +4203,13 @@ function pinCliCommand2(packageManager, root) {
5182
4203
  }
5183
4204
  }
5184
4205
  function rel(root, path) {
5185
- const value = relative5(root, path).replace(/\\/g, "/");
4206
+ const value = relative4(root, path).replace(/\\/g, "/");
5186
4207
  return value || ".";
5187
4208
  }
5188
4209
  function hasAnyFile(dir, names) {
5189
4210
  for (const name of names) {
5190
- const path = join19(dir, name);
5191
- if (existsSync17(path)) return name;
4211
+ const path = join17(dir, name);
4212
+ if (existsSync15(path)) return name;
5192
4213
  }
5193
4214
  return null;
5194
4215
  }
@@ -5210,7 +4231,7 @@ function detectDesignAuthority(workspaceRoot, appRoot) {
5210
4231
  "libs/design-system"
5211
4232
  ];
5212
4233
  for (const candidate of sharedCandidates) {
5213
- if (existsSync17(join19(workspaceRoot, candidate))) found.add(candidate);
4234
+ if (existsSync15(join17(workspaceRoot, candidate))) found.add(candidate);
5214
4235
  }
5215
4236
  for (const root of [appRoot, workspaceRoot]) {
5216
4237
  const label = root === appRoot ? "app" : "workspace";
@@ -5221,9 +4242,9 @@ function detectDesignAuthority(workspaceRoot, appRoot) {
5221
4242
  "tailwind.config.cjs"
5222
4243
  ]);
5223
4244
  if (tailwind) found.add(`${label}:${tailwind}`);
5224
- if (existsSync17(join19(root, ".storybook"))) found.add(`${label}:.storybook`);
5225
- if (existsSync17(join19(root, "components", "ui"))) found.add(`${label}:components/ui`);
5226
- if (existsSync17(join19(root, "src", "components", "ui"))) found.add(`${label}:src/components/ui`);
4245
+ if (existsSync15(join17(root, ".storybook"))) found.add(`${label}:.storybook`);
4246
+ if (existsSync15(join17(root, "components", "ui"))) found.add(`${label}:components/ui`);
4247
+ if (existsSync15(join17(root, "src", "components", "ui"))) found.add(`${label}:src/components/ui`);
5227
4248
  if (packageHasDependency(root, ["@storybook/react", "storybook"]))
5228
4249
  found.add(`${label}:storybook`);
5229
4250
  if (packageHasDependency(root, ["tailwindcss"])) found.add(`${label}:tailwindcss`);
@@ -5235,18 +4256,18 @@ function detectDesignAuthority(workspaceRoot, appRoot) {
5235
4256
  }
5236
4257
  function findCiFiles(root) {
5237
4258
  const files = [];
5238
- const workflows = join19(root, ".github", "workflows");
5239
- if (existsSync17(workflows)) {
5240
- for (const entry of readdirSync6(workflows, { withFileTypes: true })) {
4259
+ const workflows = join17(root, ".github", "workflows");
4260
+ if (existsSync15(workflows)) {
4261
+ for (const entry of readdirSync5(workflows, { withFileTypes: true })) {
5241
4262
  if (!entry.isFile()) continue;
5242
- const path = join19(workflows, entry.name);
5243
- const text = readFileSync14(path, "utf-8");
4263
+ const path = join17(workflows, entry.name);
4264
+ const text = readFileSync12(path, "utf-8");
5244
4265
  if (text.includes("decantr")) files.push(rel(root, path));
5245
4266
  }
5246
4267
  }
5247
4268
  for (const candidate of ["Jenkinsfile", ".gitlab-ci.yml", "azure-pipelines.yml", "BUILD"]) {
5248
- const path = join19(root, candidate);
5249
- if (existsSync17(path) && readFileSync14(path, "utf-8").includes("decantr")) {
4269
+ const path = join17(root, candidate);
4270
+ if (existsSync15(path) && readFileSync12(path, "utf-8").includes("decantr")) {
5250
4271
  files.push(candidate);
5251
4272
  }
5252
4273
  }
@@ -5372,6 +4393,28 @@ function deriveAdoptionLane(input) {
5372
4393
  nextChoice: "Use task and verify to keep generated routes aligned with the compiled contract."
5373
4394
  };
5374
4395
  }
4396
+ function inspectGraphArtifacts(appRoot, workspaceRoot) {
4397
+ const snapshotPath = join17(appRoot, ".decantr", "graph", "graph.snapshot.json");
4398
+ const capsulePath = join17(appRoot, ".decantr", "graph", "contract-capsule.json");
4399
+ try {
4400
+ const artifacts = buildGraphArtifacts(appRoot);
4401
+ return {
4402
+ snapshotPresent: existsSync15(snapshotPath),
4403
+ capsulePresent: existsSync15(capsulePath),
4404
+ artifactsCurrent: artifacts ? artifacts.staleArtifacts.length === 0 : null,
4405
+ staleArtifacts: artifacts ? artifacts.staleArtifacts.map((path) => rel(workspaceRoot, path)) : [],
4406
+ error: null
4407
+ };
4408
+ } catch (error3) {
4409
+ return {
4410
+ snapshotPresent: existsSync15(snapshotPath),
4411
+ capsulePresent: existsSync15(capsulePath),
4412
+ artifactsCurrent: false,
4413
+ staleArtifacts: [],
4414
+ error: error3.message
4415
+ };
4416
+ }
4417
+ }
5375
4418
  function buildDoctorReport(root, args) {
5376
4419
  let projectArg;
5377
4420
  for (let index = 1; index < args.length; index += 1) {
@@ -5383,18 +4426,18 @@ function buildDoctorReport(root, args) {
5383
4426
  const workspaceInfo = resolveWorkspaceInfo(root, projectArg);
5384
4427
  const workspaceRoot = workspaceInfo.workspaceRoot;
5385
4428
  const appRoot = workspaceMode ? workspaceRoot : workspaceInfo.appRoot;
5386
- const projectMissing = Boolean(projectArg && !existsSync17(appRoot));
4429
+ const projectMissing = Boolean(projectArg && !existsSync15(appRoot));
5387
4430
  const projectPath = appRoot === workspaceRoot ? null : rel(workspaceRoot, appRoot);
5388
4431
  const packageManager = detectPackageManager3(workspaceRoot);
5389
4432
  const cliDependency = localCliDependency(workspaceRoot);
5390
4433
  const detected = detectProject(appRoot);
5391
- const projectJson = readJson2(join19(appRoot, ".decantr", "project.json"));
5392
- const essence = readJson2(join19(appRoot, "decantr.essence.json"));
4434
+ const projectJson = readJson2(join17(appRoot, ".decantr", "project.json"));
4435
+ const essence = readJson2(join17(appRoot, "decantr.essence.json"));
5393
4436
  const essenceVersion = essence && typeof essence === "object" && "version" in essence ? String(essence.version) : null;
5394
- const contextDir = join19(appRoot, ".decantr", "context");
5395
- const packManifestPresent = existsSync17(join19(contextDir, "pack-manifest.json"));
5396
- const reviewPackPresent = existsSync17(join19(contextDir, "review-pack.json"));
5397
- const artifactReadmePresent = existsSync17(join19(appRoot, ARTIFACT_README_PATH));
4437
+ const contextDir = join17(appRoot, ".decantr", "context");
4438
+ const packManifestPresent = existsSync15(join17(contextDir, "pack-manifest.json"));
4439
+ const reviewPackPresent = existsSync15(join17(contextDir, "review-pack.json"));
4440
+ const artifactReadmePresent = existsSync15(join17(appRoot, ARTIFACT_README_PATH));
5398
4441
  const projects = listWorkspaceProjects(workspaceRoot);
5399
4442
  const candidates = listWorkspaceCandidates(workspaceRoot, projects).map((candidate) => ({
5400
4443
  path: candidate.path,
@@ -5405,22 +4448,23 @@ function buildDoctorReport(root, args) {
5405
4448
  const workflowMode = projectJson?.initialized?.workflowMode ?? null;
5406
4449
  const adoptionMode = projectJson?.initialized?.adoptionMode ?? null;
5407
4450
  const packHydrationOptional = adoptionMode === "contract-only" || adoptionMode === "style-bridge";
5408
- const localPatternsPresent = existsSync17(localPatternsPath(appRoot));
5409
- const localRulesPresent = existsSync17(localRulesPath(appRoot));
5410
- const styleBridgePresent = existsSync17(styleBridgePath(appRoot));
5411
- const styleBridgeProposalPresent = existsSync17(styleBridgeProposalPath(appRoot));
4451
+ const localPatternsPresent = existsSync15(localPatternsPath(appRoot));
4452
+ const localRulesPresent = existsSync15(localRulesPath(appRoot));
4453
+ const styleBridgePresent = existsSync15(styleBridgePath(appRoot));
4454
+ const styleBridgeProposalPresent = existsSync15(styleBridgeProposalPath(appRoot));
4455
+ const graphArtifacts = inspectGraphArtifacts(appRoot, workspaceRoot);
5412
4456
  const missingPackReferences = workspaceMode ? projects.flatMap(
5413
- (project) => collectMissingPackManifestFiles(join19(workspaceRoot, project.path)).map(
4457
+ (project) => collectMissingPackManifestFiles(join17(workspaceRoot, project.path)).map(
5414
4458
  (missing) => `${project.path}/${missing.relativePath}`
5415
4459
  )
5416
4460
  ) : collectMissingPackManifestFiles(appRoot).map((missing) => missing.relativePath);
5417
4461
  const workspaceProjectsMissingManifest = workspaceMode ? projects.map((project) => project.path).filter((projectPath2) => {
5418
- const projectContextDir = join19(workspaceRoot, projectPath2, ".decantr", "context");
5419
- return !isContractOnlyProject(join19(workspaceRoot, projectPath2)) && !existsSync17(join19(projectContextDir, "pack-manifest.json"));
4462
+ const projectContextDir = join17(workspaceRoot, projectPath2, ".decantr", "context");
4463
+ return !isContractOnlyProject(join17(workspaceRoot, projectPath2)) && !existsSync15(join17(projectContextDir, "pack-manifest.json"));
5420
4464
  }) : [];
5421
4465
  const workspaceProjectsMissingReviewPack = workspaceMode ? projects.map((project) => project.path).filter((projectPath2) => {
5422
- const projectContextDir = join19(workspaceRoot, projectPath2, ".decantr", "context");
5423
- return !isContractOnlyProject(join19(workspaceRoot, projectPath2)) && !existsSync17(join19(projectContextDir, "review-pack.json"));
4466
+ const projectContextDir = join17(workspaceRoot, projectPath2, ".decantr", "context");
4467
+ return !isContractOnlyProject(join17(workspaceRoot, projectPath2)) && !existsSync15(join17(projectContextDir, "review-pack.json"));
5424
4468
  }) : [];
5425
4469
  const issues = [];
5426
4470
  if (projectMissing) {
@@ -5437,7 +4481,7 @@ function buildDoctorReport(root, args) {
5437
4481
  message: "No decantr.essence.json found for this app.",
5438
4482
  nextCommand: projectPath ? `decantr adopt --project ${projectPath} --yes` : "decantr adopt --yes"
5439
4483
  });
5440
- } else if (essenceVersion && !isV44(essence)) {
4484
+ } else if (essenceVersion && !isV43(essence)) {
5441
4485
  issues.push({
5442
4486
  category: "migration",
5443
4487
  severity: "error",
@@ -5477,7 +4521,7 @@ function buildDoctorReport(root, args) {
5477
4521
  nextCommand: "decantr refresh"
5478
4522
  });
5479
4523
  }
5480
- if (essenceVersion === "4.0.0" && !packHydrationOptional && (!existsSync17(contextDir) || !packManifestPresent)) {
4524
+ if (essenceVersion === "4.0.0" && !packHydrationOptional && (!existsSync15(contextDir) || !packManifestPresent)) {
5481
4525
  issues.push({
5482
4526
  category: "generated-artifact",
5483
4527
  severity: "warn",
@@ -5493,6 +4537,14 @@ function buildDoctorReport(root, args) {
5493
4537
  nextCommand: projectPath ? `decantr registry compile-packs ${projectPath}/decantr.essence.json --write-context` : "decantr registry compile-packs decantr.essence.json --write-context"
5494
4538
  });
5495
4539
  }
4540
+ if (essenceVersion === "4.0.0" && (Boolean(projectJson) || graphArtifacts.snapshotPresent || graphArtifacts.capsulePresent) && (graphArtifacts.error || graphArtifacts.artifactsCurrent === false)) {
4541
+ issues.push({
4542
+ category: "graph",
4543
+ severity: "warn",
4544
+ message: graphArtifacts.error ? `Typed Contract graph could not be derived: ${graphArtifacts.error}` : "Typed Contract graph artifacts are missing or stale.",
4545
+ nextCommand: projectPath ? `decantr graph --project ${projectPath}` : "decantr graph"
4546
+ });
4547
+ }
5496
4548
  if (workspaceMode && (workspaceProjectsMissingManifest.length > 0 || workspaceProjectsMissingReviewPack.length > 0 || missingPackReferences.length > 0)) {
5497
4549
  issues.push({
5498
4550
  category: "generated-artifact",
@@ -5562,6 +4614,10 @@ function buildDoctorReport(root, args) {
5562
4614
  recommendedNextCommands,
5563
4615
  issues.find((issue) => issue.category === "ci" && issue.message.includes("not pinned"))?.nextCommand
5564
4616
  );
4617
+ appendUnique(
4618
+ recommendedNextCommands,
4619
+ issues.find((issue) => issue.category === "graph")?.nextCommand
4620
+ );
5565
4621
  if (workflowMode === "brownfield-attach" && !localPatternsPresent) {
5566
4622
  appendUnique(recommendedNextCommands, `decantr codify --from-audit${projectFlag}`);
5567
4623
  appendUnique(recommendedNextCommands, `decantr codify --accept${projectFlag}`);
@@ -5611,11 +4667,16 @@ function buildDoctorReport(root, args) {
5611
4667
  },
5612
4668
  generatedArtifacts: {
5613
4669
  contextDirPresent: workspaceMode ? projects.some(
5614
- (project) => existsSync17(join19(workspaceRoot, project.path, ".decantr", "context"))
5615
- ) : existsSync17(contextDir),
4670
+ (project) => existsSync15(join17(workspaceRoot, project.path, ".decantr", "context"))
4671
+ ) : existsSync15(contextDir),
5616
4672
  packManifestPresent: workspaceMode ? projects.length > 0 && workspaceProjectsMissingManifest.length === 0 : packManifestPresent,
5617
4673
  reviewPackPresent: workspaceMode ? projects.length > 0 && workspaceProjectsMissingReviewPack.length === 0 : reviewPackPresent,
5618
- missingReferencedFiles: missingPackReferences.slice(0, 25)
4674
+ missingReferencedFiles: missingPackReferences.slice(0, 25),
4675
+ graphSnapshotPresent: graphArtifacts.snapshotPresent,
4676
+ graphCapsulePresent: graphArtifacts.capsulePresent,
4677
+ graphArtifactsCurrent: graphArtifacts.artifactsCurrent,
4678
+ graphStaleArtifacts: graphArtifacts.staleArtifacts.slice(0, 25),
4679
+ graphError: graphArtifacts.error
5619
4680
  },
5620
4681
  localLaw: {
5621
4682
  patternsPresent: localPatternsPresent,
@@ -5624,7 +4685,7 @@ function buildDoctorReport(root, args) {
5624
4685
  styleBridgeProposalPresent
5625
4686
  },
5626
4687
  visualEvidence: {
5627
- manifestPresent: existsSync17(join19(appRoot, ".decantr", "evidence", "visual-manifest.json"))
4688
+ manifestPresent: existsSync15(join17(appRoot, ".decantr", "evidence", "visual-manifest.json"))
5628
4689
  },
5629
4690
  lane,
5630
4691
  designAuthority,
@@ -5668,6 +4729,8 @@ function formatDoctorText(report) {
5668
4729
  ` Review pack: ${report.generatedArtifacts.reviewPackPresent ? "present" : "missing"}`,
5669
4730
  ` Manifest references: ${report.generatedArtifacts.missingReferencedFiles.length === 0 ? "complete" : `${report.generatedArtifacts.missingReferencedFiles.length} missing`}`,
5670
4731
  ` Artifact guide: ${report.project.artifactReadmePresent ? "present" : "missing"}`,
4732
+ ` Typed graph: ${report.generatedArtifacts.graphError ? "error" : report.generatedArtifacts.graphArtifactsCurrent === true ? "current" : report.generatedArtifacts.graphArtifactsCurrent === false ? "stale or missing" : "not generated"}`,
4733
+ ` Graph capsule: ${report.generatedArtifacts.graphCapsulePresent ? "present" : "missing"}`,
5671
4734
  "",
5672
4735
  `${BOLD3}Local Law:${RESET4}`,
5673
4736
  ` Local patterns: ${report.localLaw.patternsPresent ? "present" : "missing"}`,
@@ -5732,17 +4795,17 @@ async function cmdDoctor(args = ["doctor"], root = process.cwd()) {
5732
4795
  }
5733
4796
 
5734
4797
  // src/commands/export.ts
5735
- import { existsSync as existsSync18, mkdirSync as mkdirSync12, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "fs";
5736
- import { dirname as dirname5, isAbsolute, join as join20 } from "path";
4798
+ import { existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "fs";
4799
+ import { dirname as dirname5, isAbsolute, join as join18 } from "path";
5737
4800
  var GREEN5 = "\x1B[32m";
5738
4801
  var RED4 = "\x1B[31m";
5739
4802
  var DIM5 = "\x1B[2m";
5740
4803
  var RESET5 = "\x1B[0m";
5741
4804
  function readAdoptionMode(projectRoot) {
5742
- const projectJsonPath = join20(projectRoot, ".decantr", "project.json");
5743
- if (!existsSync18(projectJsonPath)) return null;
4805
+ const projectJsonPath = join18(projectRoot, ".decantr", "project.json");
4806
+ if (!existsSync16(projectJsonPath)) return null;
5744
4807
  try {
5745
- const parsed = JSON.parse(readFileSync15(projectJsonPath, "utf-8"));
4808
+ const parsed = JSON.parse(readFileSync13(projectJsonPath, "utf-8"));
5746
4809
  return typeof parsed.initialized?.adoptionMode === "string" ? parsed.initialized.adoptionMode : null;
5747
4810
  } catch {
5748
4811
  return null;
@@ -5750,7 +4813,7 @@ function readAdoptionMode(projectRoot) {
5750
4813
  }
5751
4814
  function resolveOutputPath(projectRoot, output, fallback) {
5752
4815
  if (!output) return fallback;
5753
- return isAbsolute(output) ? output : join20(projectRoot, output);
4816
+ return isAbsolute(output) ? output : join18(projectRoot, output);
5754
4817
  }
5755
4818
  var SHADCN_MAP = {
5756
4819
  "--d-bg": "--background",
@@ -5911,21 +4974,21 @@ function generateFigmaTokens(tokens) {
5911
4974
  `;
5912
4975
  }
5913
4976
  async function cmdExport(target, projectRoot, options = {}) {
5914
- const essencePath = join20(projectRoot, "decantr.essence.json");
5915
- const tokensPath = join20(projectRoot, "src", "styles", "tokens.css");
5916
- if (!existsSync18(essencePath)) {
4977
+ const essencePath = join18(projectRoot, "decantr.essence.json");
4978
+ const tokensPath = join18(projectRoot, "src", "styles", "tokens.css");
4979
+ if (!existsSync16(essencePath)) {
5917
4980
  console.error(`${RED4}No decantr.essence.json found. Run \`decantr init\` first.${RESET5}`);
5918
4981
  process.exitCode = 1;
5919
4982
  return;
5920
4983
  }
5921
- if (!existsSync18(tokensPath)) {
4984
+ if (!existsSync16(tokensPath)) {
5922
4985
  const adoptionMode = readAdoptionMode(projectRoot);
5923
4986
  const message = adoptionMode === "contract-only" ? `No src/styles/tokens.css found. This export reads Decantr CSS tokens, but this project is contract-only Brownfield and may intentionally use its own Tailwind/Sass token system. Export from the app's token source or adopt a style bridge before using \`decantr export --to ${target}\`.` : "No src/styles/tokens.css found. Run `decantr refresh` to generate tokens.";
5924
4987
  console.error(`${RED4}${message}${RESET5}`);
5925
4988
  process.exitCode = 1;
5926
4989
  return;
5927
4990
  }
5928
- const tokensCSS = readFileSync15(tokensPath, "utf-8");
4991
+ const tokensCSS = readFileSync13(tokensPath, "utf-8");
5929
4992
  const tokens = parseTokensCSS(tokensCSS);
5930
4993
  if (tokens.size === 0) {
5931
4994
  console.error(`${RED4}No --d-* tokens found in tokens.css.${RESET5}`);
@@ -5937,12 +5000,12 @@ async function cmdExport(target, projectRoot, options = {}) {
5937
5000
  const cssOut = resolveOutputPath(
5938
5001
  projectRoot,
5939
5002
  options.output,
5940
- join20(projectRoot, "src", "styles", "shadcn-theme.css")
5003
+ join18(projectRoot, "src", "styles", "shadcn-theme.css")
5941
5004
  );
5942
- const jsonOut = join20(projectRoot, "components.json");
5005
+ const jsonOut = join18(projectRoot, "components.json");
5943
5006
  ensureDir(cssOut);
5944
- writeFileSync13(cssOut, generateShadcnCSS(tokens), "utf-8");
5945
- writeFileSync13(jsonOut, generateShadcnComponentsJSON(), "utf-8");
5007
+ writeFileSync11(cssOut, generateShadcnCSS(tokens), "utf-8");
5008
+ writeFileSync11(jsonOut, generateShadcnComponentsJSON(), "utf-8");
5946
5009
  console.log(`${GREEN5}Exported shadcn theme:${RESET5}`);
5947
5010
  console.log(` ${DIM5}CSS:${RESET5} ${cssOut}`);
5948
5011
  console.log(` ${DIM5}JSON:${RESET5} ${jsonOut}`);
@@ -5952,10 +5015,10 @@ async function cmdExport(target, projectRoot, options = {}) {
5952
5015
  const out = resolveOutputPath(
5953
5016
  projectRoot,
5954
5017
  options.output,
5955
- join20(projectRoot, "tailwind.decantr.config.ts")
5018
+ join18(projectRoot, "tailwind.decantr.config.ts")
5956
5019
  );
5957
5020
  ensureDir(out);
5958
- writeFileSync13(out, generateTailwindConfig(tokens), "utf-8");
5021
+ writeFileSync11(out, generateTailwindConfig(tokens), "utf-8");
5959
5022
  console.log(`${GREEN5}Exported Tailwind config:${RESET5}`);
5960
5023
  console.log(` ${DIM5}File:${RESET5} ${out}`);
5961
5024
  break;
@@ -5964,10 +5027,10 @@ async function cmdExport(target, projectRoot, options = {}) {
5964
5027
  const out = resolveOutputPath(
5965
5028
  projectRoot,
5966
5029
  options.output,
5967
- join20(projectRoot, "decantr-tokens.css")
5030
+ join18(projectRoot, "decantr-tokens.css")
5968
5031
  );
5969
5032
  ensureDir(out);
5970
- writeFileSync13(out, generateCSSVars(tokens), "utf-8");
5033
+ writeFileSync11(out, generateCSSVars(tokens), "utf-8");
5971
5034
  console.log(`${GREEN5}Exported CSS variables:${RESET5}`);
5972
5035
  console.log(` ${DIM5}File:${RESET5} ${out}`);
5973
5036
  break;
@@ -5976,10 +5039,10 @@ async function cmdExport(target, projectRoot, options = {}) {
5976
5039
  const out = resolveOutputPath(
5977
5040
  projectRoot,
5978
5041
  options.output,
5979
- join20(projectRoot, ".decantr", "design", "figma-tokens.json")
5042
+ join18(projectRoot, ".decantr", "design", "figma-tokens.json")
5980
5043
  );
5981
5044
  ensureDir(out);
5982
- writeFileSync13(out, generateFigmaTokens(tokens), "utf-8");
5045
+ writeFileSync11(out, generateFigmaTokens(tokens), "utf-8");
5983
5046
  console.log(`${GREEN5}Exported Figma/Tokens Studio tokens:${RESET5}`);
5984
5047
  console.log(` ${DIM5}File:${RESET5} ${out}`);
5985
5048
  break;
@@ -5988,15 +5051,15 @@ async function cmdExport(target, projectRoot, options = {}) {
5988
5051
  }
5989
5052
  function ensureDir(filePath) {
5990
5053
  const dir = dirname5(filePath);
5991
- if (!existsSync18(dir)) {
5992
- mkdirSync12(dir, { recursive: true });
5054
+ if (!existsSync16(dir)) {
5055
+ mkdirSync10(dir, { recursive: true });
5993
5056
  }
5994
5057
  }
5995
5058
 
5996
5059
  // src/commands/magic.ts
5997
- import { existsSync as existsSync19 } from "fs";
5060
+ import { existsSync as existsSync17 } from "fs";
5998
5061
  import * as fs from "fs/promises";
5999
- import { join as join21 } from "path";
5062
+ import { join as join19 } from "path";
6000
5063
  var BOLD4 = "\x1B[1m";
6001
5064
  var DIM6 = "\x1B[2m";
6002
5065
  var RESET6 = "\x1B[0m";
@@ -6220,8 +5283,8 @@ async function cmdMagic(prompt, projectRoot, options) {
6220
5283
  console.log(` Archetype: ${intent.archetype}`);
6221
5284
  }
6222
5285
  console.log("");
6223
- const essencePath = join21(projectRoot, "decantr.essence.json");
6224
- if (existsSync19(essencePath)) {
5286
+ const essencePath = join19(projectRoot, "decantr.essence.json");
5287
+ if (existsSync17(essencePath)) {
6225
5288
  const projectFlag = options.projectLabel ? ` --project ${options.projectLabel}` : "";
6226
5289
  console.log(`${YELLOW4} Decantr is already attached to this project.${RESET6}`);
6227
5290
  console.log(
@@ -6254,7 +5317,7 @@ async function cmdMagic(prompt, projectRoot, options) {
6254
5317
  return;
6255
5318
  }
6256
5319
  const registryClient = new RegistryClient({
6257
- cacheDir: join21(projectRoot, ".decantr", "cache"),
5320
+ cacheDir: join19(projectRoot, ".decantr", "cache"),
6258
5321
  apiUrl: options.registry,
6259
5322
  offline: options.offline
6260
5323
  });
@@ -6525,14 +5588,14 @@ async function cmdMagic(prompt, projectRoot, options) {
6525
5588
  if (result.gitignoreUpdated) {
6526
5589
  console.log(` ${dim(".gitignore updated")}`);
6527
5590
  }
6528
- const contextDir = join21(projectRoot, ".decantr", "context");
5591
+ const contextDir = join19(projectRoot, ".decantr", "context");
6529
5592
  let sectionCount = 0;
6530
5593
  try {
6531
5594
  const files = await fs.readdir(contextDir);
6532
5595
  sectionCount = files.filter((f) => f.startsWith("section-")).length;
6533
5596
  } catch {
6534
5597
  }
6535
- const treatmentsPath = join21(projectRoot, "src", "styles", "treatments.css");
5598
+ const treatmentsPath = join19(projectRoot, "src", "styles", "treatments.css");
6536
5599
  let hasLayers = false;
6537
5600
  try {
6538
5601
  const css = await fs.readFile(treatmentsPath, "utf-8");
@@ -6565,10 +5628,10 @@ ${GREEN6}${BOLD4}Quality summary:${RESET6}`);
6565
5628
  }
6566
5629
 
6567
5630
  // src/commands/migrate.ts
6568
- import { copyFileSync, existsSync as existsSync20, readFileSync as readFileSync16, writeFileSync as writeFileSync14 } from "fs";
6569
- import { join as join22 } from "path";
5631
+ import { copyFileSync, existsSync as existsSync18, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "fs";
5632
+ import { join as join20 } from "path";
6570
5633
  import {
6571
- isV4 as isV45,
5634
+ isV4 as isV44,
6572
5635
  migrateToV4,
6573
5636
  validateEssence,
6574
5637
  validateLegacyEssenceForMigration
@@ -6579,12 +5642,12 @@ var YELLOW5 = "\x1B[33m";
6579
5642
  var RESET7 = "\x1B[0m";
6580
5643
  var DIM7 = "\x1B[2m";
6581
5644
  function migrateEssenceFile(essencePath) {
6582
- if (!existsSync20(essencePath)) {
5645
+ if (!existsSync18(essencePath)) {
6583
5646
  return { success: false, error: `File not found: ${essencePath}` };
6584
5647
  }
6585
5648
  let raw;
6586
5649
  try {
6587
- raw = readFileSync16(essencePath, "utf-8");
5650
+ raw = readFileSync14(essencePath, "utf-8");
6588
5651
  } catch (e) {
6589
5652
  return { success: false, error: `Could not read ${essencePath}: ${e.message}` };
6590
5653
  }
@@ -6594,7 +5657,7 @@ function migrateEssenceFile(essencePath) {
6594
5657
  } catch (e) {
6595
5658
  return { success: false, error: `Invalid JSON: ${e.message}` };
6596
5659
  }
6597
- if (isV45(essence)) {
5660
+ if (isV44(essence)) {
6598
5661
  return { success: true, alreadyV4: true, essence };
6599
5662
  }
6600
5663
  const preValidation = validateLegacyEssenceForMigration(essence);
@@ -6625,7 +5688,7 @@ function migrateEssenceFile(essencePath) {
6625
5688
  };
6626
5689
  }
6627
5690
  try {
6628
- writeFileSync14(essencePath, JSON.stringify(v4, null, 2) + "\n");
5691
+ writeFileSync12(essencePath, JSON.stringify(v4, null, 2) + "\n");
6629
5692
  } catch (e) {
6630
5693
  return {
6631
5694
  success: false,
@@ -6646,8 +5709,8 @@ async function cmdMigrate(projectRoot = process.cwd(), args = []) {
6646
5709
  process.exitCode = 1;
6647
5710
  return;
6648
5711
  }
6649
- const essencePath = join22(projectRoot, "decantr.essence.json");
6650
- if (!existsSync20(essencePath)) {
5712
+ const essencePath = join20(projectRoot, "decantr.essence.json");
5713
+ if (!existsSync18(essencePath)) {
6651
5714
  console.error(`${RED5}No decantr.essence.json found. Run \`decantr init\` first.${RESET7}`);
6652
5715
  process.exitCode = 1;
6653
5716
  return;
@@ -6669,7 +5732,7 @@ async function cmdMigrate(projectRoot = process.cwd(), args = []) {
6669
5732
  }
6670
5733
  if (result.essence) {
6671
5734
  const registryClient = new RegistryClient({
6672
- cacheDir: join22(projectRoot, ".decantr", "cache")
5735
+ cacheDir: join20(projectRoot, ".decantr", "cache")
6673
5736
  });
6674
5737
  await refreshDerivedFiles(projectRoot, result.essence, registryClient);
6675
5738
  console.log(`${GREEN7}Derived context and execution packs refreshed.${RESET7}`);
@@ -6680,50 +5743,50 @@ async function cmdMigrate(projectRoot = process.cwd(), args = []) {
6680
5743
 
6681
5744
  // src/commands/new-project.ts
6682
5745
  import { spawnSync } from "child_process";
6683
- import { existsSync as existsSync22, mkdirSync as mkdirSync14 } from "fs";
6684
- import { join as join24, resolve as resolve3 } from "path";
5746
+ import { existsSync as existsSync20, mkdirSync as mkdirSync12 } from "fs";
5747
+ import { join as join22, resolve as resolve3 } from "path";
6685
5748
  import { fileURLToPath as fileURLToPath2 } from "url";
6686
5749
 
6687
5750
  // src/offline-content.ts
6688
- import { cpSync, existsSync as existsSync21, mkdirSync as mkdirSync13 } from "fs";
6689
- import { join as join23, resolve as resolve2 } from "path";
5751
+ import { cpSync, existsSync as existsSync19, mkdirSync as mkdirSync11 } from "fs";
5752
+ import { join as join21, resolve as resolve2 } from "path";
6690
5753
  var CONTENT_TYPES2 = ["archetypes", "blueprints", "patterns", "themes", "shells"];
6691
5754
  function copyIfExists(source, target) {
6692
- if (!existsSync21(source)) return false;
5755
+ if (!existsSync19(source)) return false;
6693
5756
  if (resolve2(source) === resolve2(target)) return true;
6694
5757
  cpSync(source, target, { recursive: true });
6695
5758
  return true;
6696
5759
  }
6697
5760
  function hydrateContentRoot(projectDir, contentRoot) {
6698
- if (!existsSync21(contentRoot)) return false;
6699
- const customRoot = join23(projectDir, ".decantr", "custom");
6700
- const cacheRoot = join23(projectDir, ".decantr", "cache", "@official");
6701
- mkdirSync13(customRoot, { recursive: true });
6702
- mkdirSync13(cacheRoot, { recursive: true });
5761
+ if (!existsSync19(contentRoot)) return false;
5762
+ const customRoot = join21(projectDir, ".decantr", "custom");
5763
+ const cacheRoot = join21(projectDir, ".decantr", "cache", "@official");
5764
+ mkdirSync11(customRoot, { recursive: true });
5765
+ mkdirSync11(cacheRoot, { recursive: true });
6703
5766
  let copiedAny = false;
6704
5767
  for (const type of CONTENT_TYPES2) {
6705
- const sourceDir = join23(contentRoot, type);
6706
- if (!existsSync21(sourceDir)) continue;
6707
- cpSync(sourceDir, join23(customRoot, type), { recursive: true });
6708
- cpSync(sourceDir, join23(cacheRoot, type), { recursive: true });
5768
+ const sourceDir = join21(contentRoot, type);
5769
+ if (!existsSync19(sourceDir)) continue;
5770
+ cpSync(sourceDir, join21(customRoot, type), { recursive: true });
5771
+ cpSync(sourceDir, join21(cacheRoot, type), { recursive: true });
6709
5772
  copiedAny = true;
6710
5773
  }
6711
5774
  return copiedAny;
6712
5775
  }
6713
5776
  function seedOfflineRegistry(projectDir, workspaceRoot) {
6714
- const projectDecantrRoot = join23(projectDir, ".decantr");
6715
- mkdirSync13(projectDecantrRoot, { recursive: true });
5777
+ const projectDecantrRoot = join21(projectDir, ".decantr");
5778
+ mkdirSync11(projectDecantrRoot, { recursive: true });
6716
5779
  const configuredContentRoot = process.env.DECANTR_CONTENT_DIR ? resolve2(process.env.DECANTR_CONTENT_DIR) : null;
6717
5780
  if (configuredContentRoot && hydrateContentRoot(projectDir, configuredContentRoot)) {
6718
5781
  return { seeded: true, strategy: "configured-content-root" };
6719
5782
  }
6720
5783
  const copiedCache = copyIfExists(
6721
- join23(workspaceRoot, ".decantr", "cache"),
6722
- join23(projectDecantrRoot, "cache")
5784
+ join21(workspaceRoot, ".decantr", "cache"),
5785
+ join21(projectDecantrRoot, "cache")
6723
5786
  );
6724
5787
  const copiedCustom = copyIfExists(
6725
- join23(workspaceRoot, ".decantr", "custom"),
6726
- join23(projectDecantrRoot, "custom")
5788
+ join21(workspaceRoot, ".decantr", "custom"),
5789
+ join21(projectDecantrRoot, "custom")
6727
5790
  );
6728
5791
  if (copiedCache || copiedCustom) {
6729
5792
  return { seeded: true, strategy: "workspace-cache" };
@@ -6767,7 +5830,10 @@ function validatePassThroughFlagValue(flag, value) {
6767
5830
  if (value.length > 512) {
6768
5831
  throw new Error(`--${flag} is too long.`);
6769
5832
  }
6770
- if (/[\u0000-\u001f\u007f]/.test(value)) {
5833
+ if (Array.from(value).some((character) => {
5834
+ const code = character.charCodeAt(0);
5835
+ return code <= 31 || code === 127;
5836
+ })) {
6771
5837
  throw new Error(`--${flag} contains unsupported control characters.`);
6772
5838
  }
6773
5839
  return value;
@@ -6836,7 +5902,7 @@ function runArgvCommand(command, args, cwd) {
6836
5902
  }
6837
5903
  function resolveInitCommand(initFlags) {
6838
5904
  const bundledCliEntrypoint = fileURLToPath2(new URL("./bin.js", import.meta.url));
6839
- const cliEntrypoint = existsSync22(bundledCliEntrypoint) ? bundledCliEntrypoint : process.argv[1] && existsSync22(process.argv[1]) ? process.argv[1] : null;
5905
+ const cliEntrypoint = existsSync20(bundledCliEntrypoint) ? bundledCliEntrypoint : process.argv[1] && existsSync20(process.argv[1]) ? process.argv[1] : null;
6840
5906
  if (cliEntrypoint) {
6841
5907
  return {
6842
5908
  command: process.execPath,
@@ -6864,7 +5930,7 @@ async function cmdNewProject(projectName, options) {
6864
5930
  process.exitCode = 1;
6865
5931
  return;
6866
5932
  }
6867
- if (existsSync22(projectDir)) {
5933
+ if (existsSync20(projectDir)) {
6868
5934
  console.error(error(`Directory "${projectName}" already exists.`));
6869
5935
  process.exitCode = 1;
6870
5936
  return;
@@ -6878,7 +5944,7 @@ async function cmdNewProject(projectName, options) {
6878
5944
  return;
6879
5945
  }
6880
5946
  console.log(heading(`Creating ${projectName}...`));
6881
- mkdirSync14(projectDir, { recursive: true });
5947
+ mkdirSync12(projectDir, { recursive: true });
6882
5948
  console.log(dim2(` Created ${projectName}/`));
6883
5949
  const title = projectName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
6884
5950
  if (shouldBootstrapRuntime && bootstrapAdapter) {
@@ -6977,21 +6043,21 @@ ${YELLOW6}Decantr init encountered issues. Run \`decantr init\` manually inside
6977
6043
  }
6978
6044
  }
6979
6045
  function detectPackageManager4() {
6980
- if (existsSync22(join24(process.cwd(), "pnpm-lock.yaml")) || existsSync22(join24(process.cwd(), "pnpm-workspace.yaml"))) {
6046
+ if (existsSync20(join22(process.cwd(), "pnpm-lock.yaml")) || existsSync20(join22(process.cwd(), "pnpm-workspace.yaml"))) {
6981
6047
  return "pnpm";
6982
6048
  }
6983
- if (existsSync22(join24(process.cwd(), "yarn.lock"))) {
6049
+ if (existsSync20(join22(process.cwd(), "yarn.lock"))) {
6984
6050
  return "yarn";
6985
6051
  }
6986
- if (existsSync22(join24(process.cwd(), "bun.lockb")) || existsSync22(join24(process.cwd(), "bun.lock"))) {
6052
+ if (existsSync20(join22(process.cwd(), "bun.lockb")) || existsSync20(join22(process.cwd(), "bun.lock"))) {
6987
6053
  return "bun";
6988
6054
  }
6989
6055
  return "npm";
6990
6056
  }
6991
6057
 
6992
6058
  // src/commands/publish.ts
6993
- import { existsSync as existsSync23, readFileSync as readFileSync17 } from "fs";
6994
- import { join as join25 } from "path";
6059
+ import { existsSync as existsSync21, readFileSync as readFileSync15 } from "fs";
6060
+ import { join as join23 } from "path";
6995
6061
  import {
6996
6062
  API_CONTENT_TYPE_TO_CONTENT_TYPE,
6997
6063
  CONTENT_TYPE_TO_API_CONTENT_TYPE as CONTENT_TYPE_TO_API_CONTENT_TYPE2,
@@ -7007,8 +6073,8 @@ async function cmdPublish(type, name, projectRoot = process.cwd()) {
7007
6073
  }
7008
6074
  const singularType = API_CONTENT_TYPE_TO_CONTENT_TYPE[type] || type;
7009
6075
  const pluralType = CONTENT_TYPE_TO_API_CONTENT_TYPE2[type] || CONTENT_TYPE_TO_API_CONTENT_TYPE2[singularType] || `${type}s`;
7010
- const customPath = join25(projectRoot, ".decantr", "custom", pluralType, `${name}.json`);
7011
- if (!existsSync23(customPath)) {
6076
+ const customPath = join23(projectRoot, ".decantr", "custom", pluralType, `${name}.json`);
6077
+ if (!existsSync21(customPath)) {
7012
6078
  console.error(`Custom ${singularType} "${name}" not found at ${customPath}`);
7013
6079
  console.error(`Create one first: decantr create ${singularType} ${name}`);
7014
6080
  process.exitCode = 1;
@@ -7016,7 +6082,7 @@ async function cmdPublish(type, name, projectRoot = process.cwd()) {
7016
6082
  }
7017
6083
  let data;
7018
6084
  try {
7019
- data = JSON.parse(readFileSync17(customPath, "utf-8"));
6085
+ data = JSON.parse(readFileSync15(customPath, "utf-8"));
7020
6086
  } catch {
7021
6087
  console.error(`Failed to parse ${customPath}`);
7022
6088
  process.exitCode = 1;
@@ -7055,22 +6121,22 @@ async function cmdPublish(type, name, projectRoot = process.cwd()) {
7055
6121
 
7056
6122
  // src/commands/refresh.ts
7057
6123
  import { createHash } from "crypto";
7058
- import { existsSync as existsSync24, readdirSync as readdirSync7, readFileSync as readFileSync18, statSync as statSync6 } from "fs";
7059
- import { isAbsolute as isAbsolute2, join as join26, relative as relative6 } from "path";
6124
+ import { existsSync as existsSync22, readdirSync as readdirSync6, readFileSync as readFileSync16, statSync as statSync5 } from "fs";
6125
+ import { isAbsolute as isAbsolute2, join as join24, relative as relative5 } from "path";
6126
+ import { isV4 as isV45 } from "@decantr/essence-spec";
7060
6127
  import { collectMissingPackManifestFiles as collectMissingPackManifestFiles2 } from "@decantr/verifier";
7061
- import { isV4 as isV46 } from "@decantr/essence-spec";
7062
6128
  var GREEN9 = "\x1B[32m";
7063
6129
  var RED7 = "\x1B[31m";
7064
6130
  var DIM9 = "\x1B[2m";
7065
6131
  var RESET9 = "\x1B[0m";
7066
6132
  function hashFile(path) {
7067
- return createHash("sha256").update(readFileSync18(path)).digest("hex");
6133
+ return createHash("sha256").update(readFileSync16(path)).digest("hex");
7068
6134
  }
7069
6135
  function walkFiles(dir) {
7070
- if (!existsSync24(dir)) return [];
6136
+ if (!existsSync22(dir)) return [];
7071
6137
  const files = [];
7072
- for (const entry of readdirSync7(dir, { withFileTypes: true })) {
7073
- const path = join26(dir, entry.name);
6138
+ for (const entry of readdirSync6(dir, { withFileTypes: true })) {
6139
+ const path = join24(dir, entry.name);
7074
6140
  if (entry.isDirectory()) {
7075
6141
  files.push(...walkFiles(path));
7076
6142
  } else if (entry.isFile()) {
@@ -7081,10 +6147,10 @@ function walkFiles(dir) {
7081
6147
  }
7082
6148
  function trackedGeneratedFiles(projectRoot) {
7083
6149
  return [
7084
- join26(projectRoot, "DECANTR.md"),
7085
- ...walkFiles(join26(projectRoot, ".decantr", "context")),
7086
- ...["global.css", "tokens.css", "treatments.css", "decantr-bridge.css"].map((file) => join26(projectRoot, "src", "styles", file)).filter((path) => existsSync24(path))
7087
- ].filter((path) => existsSync24(path));
6150
+ join24(projectRoot, "DECANTR.md"),
6151
+ ...walkFiles(join24(projectRoot, ".decantr", "context")),
6152
+ ...["global.css", "tokens.css", "treatments.css", "decantr-bridge.css"].map((file) => join24(projectRoot, "src", "styles", file)).filter((path) => existsSync22(path))
6153
+ ].filter((path) => existsSync22(path));
7088
6154
  }
7089
6155
  function snapshotGeneratedFiles(projectRoot) {
7090
6156
  const map = /* @__PURE__ */ new Map();
@@ -7100,40 +6166,40 @@ function snapshotGeneratedFiles(projectRoot) {
7100
6166
  }
7101
6167
  function fileMtimeMs(path) {
7102
6168
  try {
7103
- return statSync6(path).mtimeMs;
6169
+ return statSync5(path).mtimeMs;
7104
6170
  } catch {
7105
6171
  return 0;
7106
6172
  }
7107
6173
  }
7108
6174
  function newestInputMtime(projectRoot) {
7109
6175
  return Math.max(
7110
- fileMtimeMs(join26(projectRoot, "decantr.essence.json")),
7111
- fileMtimeMs(join26(projectRoot, ".decantr", "project.json"))
6176
+ fileMtimeMs(join24(projectRoot, "decantr.essence.json")),
6177
+ fileMtimeMs(join24(projectRoot, ".decantr", "project.json"))
7112
6178
  );
7113
6179
  }
7114
6180
  function isContractOnlyProject2(projectRoot) {
7115
- const projectJsonPath = join26(projectRoot, ".decantr", "project.json");
7116
- if (!existsSync24(projectJsonPath)) return false;
6181
+ const projectJsonPath = join24(projectRoot, ".decantr", "project.json");
6182
+ if (!existsSync22(projectJsonPath)) return false;
7117
6183
  try {
7118
- const projectJson = JSON.parse(readFileSync18(projectJsonPath, "utf-8"));
6184
+ const projectJson = JSON.parse(readFileSync16(projectJsonPath, "utf-8"));
7119
6185
  return projectJson.initialized?.adoptionMode === "contract-only";
7120
6186
  } catch {
7121
6187
  return false;
7122
6188
  }
7123
6189
  }
7124
6190
  function checkRefreshFreshness(projectRoot) {
7125
- const contextDir = join26(projectRoot, ".decantr", "context");
6191
+ const contextDir = join24(projectRoot, ".decantr", "context");
7126
6192
  const generated = trackedGeneratedFiles(projectRoot);
7127
6193
  const reasons = [];
7128
6194
  const packHydrationOptional = isContractOnlyProject2(projectRoot);
7129
- if (!existsSync24(join26(projectRoot, "DECANTR.md"))) reasons.push("DECANTR.md is missing.");
7130
- if (!existsSync24(contextDir)) reasons.push(".decantr/context is missing.");
7131
- if (!existsSync24(join26(contextDir, "scaffold.md"))) {
6195
+ if (!existsSync22(join24(projectRoot, "DECANTR.md"))) reasons.push("DECANTR.md is missing.");
6196
+ if (!existsSync22(contextDir)) reasons.push(".decantr/context is missing.");
6197
+ if (!existsSync22(join24(contextDir, "scaffold.md"))) {
7132
6198
  reasons.push(".decantr/context/scaffold.md is missing.");
7133
6199
  }
7134
- if (!packHydrationOptional && !existsSync24(join26(contextDir, "pack-manifest.json"))) {
6200
+ if (!packHydrationOptional && !existsSync22(join24(contextDir, "pack-manifest.json"))) {
7135
6201
  reasons.push(".decantr/context/pack-manifest.json is missing.");
7136
- } else if (existsSync24(join26(contextDir, "pack-manifest.json"))) {
6202
+ } else if (existsSync22(join24(contextDir, "pack-manifest.json"))) {
7137
6203
  const missingPackFiles = collectMissingPackManifestFiles2(projectRoot);
7138
6204
  if (missingPackFiles.length > 0) {
7139
6205
  reasons.push(
@@ -7186,8 +6252,8 @@ function summarizeChanges(projectRoot, before, after) {
7186
6252
  }
7187
6253
  function displayGeneratedPath(summary, file, displayRoot) {
7188
6254
  if (!displayRoot) return file;
7189
- const absolutePath = join26(summary.projectRoot, file);
7190
- const relativePath = relative6(displayRoot, absolutePath).replace(/\\/g, "/");
6255
+ const absolutePath = join24(summary.projectRoot, file);
6256
+ const relativePath = relative5(displayRoot, absolutePath).replace(/\\/g, "/");
7191
6257
  if (relativePath && !relativePath.startsWith("..") && !isAbsolute2(relativePath)) {
7192
6258
  return relativePath;
7193
6259
  }
@@ -7221,17 +6287,17 @@ function printRefreshSummary(summary, displayRoot) {
7221
6287
  }
7222
6288
  }
7223
6289
  async function cmdRefresh(projectRoot = process.cwd(), options = {}) {
7224
- const essencePath = join26(projectRoot, "decantr.essence.json");
7225
- if (!existsSync24(essencePath)) {
6290
+ const essencePath = join24(projectRoot, "decantr.essence.json");
6291
+ if (!existsSync22(essencePath)) {
7226
6292
  console.error(`${RED7}No decantr.essence.json found. Run \`decantr init\` first.${RESET9}`);
7227
6293
  process.exitCode = 1;
7228
6294
  return;
7229
6295
  }
7230
6296
  let essence;
7231
6297
  try {
7232
- const raw = readFileSync18(essencePath, "utf-8");
6298
+ const raw = readFileSync16(essencePath, "utf-8");
7233
6299
  const parsed = JSON.parse(raw);
7234
- if (!isV46(parsed)) {
6300
+ if (!isV45(parsed)) {
7235
6301
  console.error(
7236
6302
  `${RED7}Active workflows require Essence v4.0.0. Run \`decantr migrate --to v4\` first.${RESET9}`
7237
6303
  );
@@ -7255,7 +6321,7 @@ async function cmdRefresh(projectRoot = process.cwd(), options = {}) {
7255
6321
  return;
7256
6322
  }
7257
6323
  const registryClient = new RegistryClient({
7258
- cacheDir: join26(projectRoot, ".decantr", "cache"),
6324
+ cacheDir: join24(projectRoot, ".decantr", "cache"),
7259
6325
  offline: options.offline
7260
6326
  });
7261
6327
  console.log("Regenerating derived files...\n");
@@ -7286,8 +6352,8 @@ async function cmdRefresh(projectRoot = process.cwd(), options = {}) {
7286
6352
  }
7287
6353
 
7288
6354
  // src/commands/registry-mirror.ts
7289
- import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync15 } from "fs";
7290
- import { join as join27 } from "path";
6355
+ import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync13 } from "fs";
6356
+ import { join as join25 } from "path";
7291
6357
  import { API_CONTENT_TYPES, RegistryAPIClient as RegistryAPIClient2 } from "@decantr/registry";
7292
6358
  var GREEN10 = "\x1B[32m";
7293
6359
  var RED8 = "\x1B[31m";
@@ -7315,7 +6381,7 @@ async function cmdRegistryMirror(projectRoot, options = {}) {
7315
6381
  process.exitCode = 1;
7316
6382
  return;
7317
6383
  }
7318
- const cacheDir = join27(projectRoot, ".decantr", "cache");
6384
+ const cacheDir = join25(projectRoot, ".decantr", "cache");
7319
6385
  const counts = {};
7320
6386
  const failed = [];
7321
6387
  console.log(`
@@ -7325,19 +6391,19 @@ Mirroring registry content to ${DIM10}.decantr/cache/${RESET10}
7325
6391
  try {
7326
6392
  const result = await apiClient.listContent(type, { namespace: "@official" });
7327
6393
  const items = result.items;
7328
- const typeDir = join27(cacheDir, "@official", type);
7329
- mkdirSync15(typeDir, { recursive: true });
7330
- writeFileSync15(join27(typeDir, "index.json"), JSON.stringify(result, null, 2));
6394
+ const typeDir = join25(cacheDir, "@official", type);
6395
+ mkdirSync13(typeDir, { recursive: true });
6396
+ writeFileSync13(join25(typeDir, "index.json"), JSON.stringify(result, null, 2));
7331
6397
  let itemCount = 0;
7332
6398
  for (const item of items) {
7333
6399
  const slug = item.slug || item.id;
7334
6400
  if (!slug) continue;
7335
6401
  try {
7336
6402
  const fullItem = await apiClient.getContent(type, "@official", slug);
7337
- writeFileSync15(join27(typeDir, `${slug}.json`), JSON.stringify(fullItem, null, 2));
6403
+ writeFileSync13(join25(typeDir, `${slug}.json`), JSON.stringify(fullItem, null, 2));
7338
6404
  itemCount++;
7339
6405
  } catch {
7340
- writeFileSync15(join27(typeDir, `${slug}.json`), JSON.stringify(item, null, 2));
6406
+ writeFileSync13(join25(typeDir, `${slug}.json`), JSON.stringify(item, null, 2));
7341
6407
  itemCount++;
7342
6408
  }
7343
6409
  }
@@ -7352,8 +6418,8 @@ Mirroring registry content to ${DIM10}.decantr/cache/${RESET10}
7352
6418
  mirrored_at: (/* @__PURE__ */ new Date()).toISOString(),
7353
6419
  counts
7354
6420
  };
7355
- mkdirSync15(join27(cacheDir), { recursive: true });
7356
- writeFileSync15(join27(cacheDir, "mirror-manifest.json"), JSON.stringify(manifest, null, 2));
6421
+ mkdirSync13(join25(cacheDir), { recursive: true });
6422
+ writeFileSync13(join25(cacheDir, "mirror-manifest.json"), JSON.stringify(manifest, null, 2));
7357
6423
  const totalItems = Object.values(counts).reduce((a, b) => a + b, 0);
7358
6424
  console.log("");
7359
6425
  if (failed.length > 0) {
@@ -7370,30 +6436,30 @@ Mirroring registry content to ${DIM10}.decantr/cache/${RESET10}
7370
6436
  }
7371
6437
 
7372
6438
  // src/commands/remove.ts
7373
- import { existsSync as existsSync26, readFileSync as readFileSync19, rmSync as rmSync2, writeFileSync as writeFileSync16 } from "fs";
7374
- import { join as join28 } from "path";
7375
- import { isV4 as isV47 } from "@decantr/essence-spec";
6439
+ import { existsSync as existsSync24, readFileSync as readFileSync17, rmSync as rmSync2, writeFileSync as writeFileSync14 } from "fs";
6440
+ import { join as join26 } from "path";
6441
+ import { isV4 as isV46 } from "@decantr/essence-spec";
7376
6442
  var GREEN11 = "\x1B[32m";
7377
6443
  var RED9 = "\x1B[31m";
7378
6444
  var YELLOW8 = "\x1B[33m";
7379
6445
  var DIM11 = "\x1B[2m";
7380
6446
  var RESET11 = "\x1B[0m";
7381
6447
  function readV4Essence2(projectRoot) {
7382
- const essencePath = join28(projectRoot, "decantr.essence.json");
7383
- if (!existsSync26(essencePath)) {
6448
+ const essencePath = join26(projectRoot, "decantr.essence.json");
6449
+ if (!existsSync24(essencePath)) {
7384
6450
  console.error(`${RED9}No decantr.essence.json found. Run \`decantr init\` first.${RESET11}`);
7385
6451
  process.exitCode = 1;
7386
6452
  return null;
7387
6453
  }
7388
6454
  let parsed;
7389
6455
  try {
7390
- parsed = JSON.parse(readFileSync19(essencePath, "utf-8"));
6456
+ parsed = JSON.parse(readFileSync17(essencePath, "utf-8"));
7391
6457
  } catch (e) {
7392
6458
  console.error(`${RED9}Could not read essence: ${e.message}${RESET11}`);
7393
6459
  process.exitCode = 1;
7394
6460
  return null;
7395
6461
  }
7396
- if (!isV47(parsed)) {
6462
+ if (!isV46(parsed)) {
7397
6463
  console.error(
7398
6464
  `${RED9}Active workflows require Essence v4.0.0. Run \`decantr migrate --to v4\` first.${RESET11}`
7399
6465
  );
@@ -7403,7 +6469,7 @@ function readV4Essence2(projectRoot) {
7403
6469
  return { essence: parsed, essencePath };
7404
6470
  }
7405
6471
  function writeEssence2(essencePath, essence) {
7406
- writeFileSync16(essencePath, JSON.stringify(essence, null, 2) + "\n");
6472
+ writeFileSync14(essencePath, JSON.stringify(essence, null, 2) + "\n");
7407
6473
  }
7408
6474
  function readFlagValue2(args, name) {
7409
6475
  const prefix = `--${name}=`;
@@ -7475,14 +6541,14 @@ async function cmdRemoveSection(sectionId, args, projectRoot = process.cwd()) {
7475
6541
  sections.splice(idx, 1);
7476
6542
  recomputeGlobalFeatures(essence);
7477
6543
  removeRoutes(essence, sectionId);
7478
- const contextFile = join28(projectRoot, ".decantr", "context", `${sectionId}.md`);
7479
- if (existsSync26(contextFile)) {
6544
+ const contextFile = join26(projectRoot, ".decantr", "context", `${sectionId}.md`);
6545
+ if (existsSync24(contextFile)) {
7480
6546
  rmSync2(contextFile);
7481
6547
  }
7482
6548
  writeEssence2(essencePath, essence);
7483
6549
  console.log(`${GREEN11}Removed section "${sectionId}".${RESET11}`);
7484
6550
  const registryClient = new RegistryClient({
7485
- cacheDir: join28(projectRoot, ".decantr", "cache")
6551
+ cacheDir: join26(projectRoot, ".decantr", "cache")
7486
6552
  });
7487
6553
  await refreshDerivedFiles(projectRoot, essence, registryClient);
7488
6554
  console.log(`${GREEN11}Derived files refreshed.${RESET11}`);
@@ -7525,7 +6591,7 @@ async function cmdRemovePage(path, args, projectRoot = process.cwd()) {
7525
6591
  }
7526
6592
  console.log(`${GREEN11}Removed page "${pageId}" from section "${resolvedSectionId}".${RESET11}`);
7527
6593
  const registryClient = new RegistryClient({
7528
- cacheDir: join28(projectRoot, ".decantr", "cache")
6594
+ cacheDir: join26(projectRoot, ".decantr", "cache")
7529
6595
  });
7530
6596
  await refreshDerivedFiles(projectRoot, essence, registryClient);
7531
6597
  console.log(`${GREEN11}Derived files refreshed.${RESET11}`);
@@ -7568,15 +6634,15 @@ async function cmdRemoveFeature(feature, args, projectRoot = process.cwd()) {
7568
6634
  const target = sectionId ? `section "${sectionId}" and global` : "global";
7569
6635
  console.log(`${GREEN11}Removed feature "${feature}" from ${target} features.${RESET11}`);
7570
6636
  const registryClient = new RegistryClient({
7571
- cacheDir: join28(projectRoot, ".decantr", "cache")
6637
+ cacheDir: join26(projectRoot, ".decantr", "cache")
7572
6638
  });
7573
6639
  await refreshDerivedFiles(projectRoot, essence, registryClient);
7574
6640
  console.log(`${GREEN11}Derived files refreshed.${RESET11}`);
7575
6641
  }
7576
6642
 
7577
6643
  // src/commands/sync-drift.ts
7578
- import { existsSync as existsSync27, readFileSync as readFileSync20, writeFileSync as writeFileSync17 } from "fs";
7579
- import { join as join29 } from "path";
6644
+ import { existsSync as existsSync25, readFileSync as readFileSync18, writeFileSync as writeFileSync15 } from "fs";
6645
+ import { join as join27 } from "path";
7580
6646
  var GREEN12 = "\x1B[32m";
7581
6647
  var RED10 = "\x1B[31m";
7582
6648
  var YELLOW9 = "\x1B[33m";
@@ -7585,8 +6651,8 @@ var DIM12 = "\x1B[2m";
7585
6651
  var BOLD6 = "\x1B[1m";
7586
6652
  var CYAN6 = "\x1B[36m";
7587
6653
  async function cmdSyncDrift(projectRoot = process.cwd()) {
7588
- const driftLogPath = join29(projectRoot, ".decantr", "drift-log.json");
7589
- if (!existsSync27(driftLogPath)) {
6654
+ const driftLogPath = join27(projectRoot, ".decantr", "drift-log.json");
6655
+ if (!existsSync25(driftLogPath)) {
7590
6656
  console.log(`${GREEN12}No drift log found \u2014 no drift recorded.${RESET12}`);
7591
6657
  console.log(
7592
6658
  `${DIM12}Drift is logged when guard violations are detected during development.${RESET12}`
@@ -7595,7 +6661,7 @@ async function cmdSyncDrift(projectRoot = process.cwd()) {
7595
6661
  }
7596
6662
  let entries;
7597
6663
  try {
7598
- entries = JSON.parse(readFileSync20(driftLogPath, "utf-8"));
6664
+ entries = JSON.parse(readFileSync18(driftLogPath, "utf-8"));
7599
6665
  } catch {
7600
6666
  console.error(`${RED10}Could not parse drift-log.json${RESET12}`);
7601
6667
  process.exitCode = 1;
@@ -7641,13 +6707,13 @@ ${BOLD6}Unresolved Drift Entries (${unresolved.length})${RESET12}
7641
6707
  console.log("");
7642
6708
  }
7643
6709
  function resolveDriftEntries(projectRoot, options) {
7644
- const driftLogPath = join29(projectRoot, ".decantr", "drift-log.json");
7645
- if (!existsSync27(driftLogPath)) {
6710
+ const driftLogPath = join27(projectRoot, ".decantr", "drift-log.json");
6711
+ if (!existsSync25(driftLogPath)) {
7646
6712
  return { success: true };
7647
6713
  }
7648
6714
  if (options.clear) {
7649
6715
  try {
7650
- writeFileSync17(driftLogPath, "[]");
6716
+ writeFileSync15(driftLogPath, "[]");
7651
6717
  return { success: true };
7652
6718
  } catch (e) {
7653
6719
  return { success: false, error: `Could not clear drift log: ${e.message}` };
@@ -7655,7 +6721,7 @@ function resolveDriftEntries(projectRoot, options) {
7655
6721
  }
7656
6722
  let entries;
7657
6723
  try {
7658
- entries = JSON.parse(readFileSync20(driftLogPath, "utf-8"));
6724
+ entries = JSON.parse(readFileSync18(driftLogPath, "utf-8"));
7659
6725
  } catch {
7660
6726
  return { success: false, error: "Could not parse drift-log.json" };
7661
6727
  }
@@ -7674,7 +6740,7 @@ function resolveDriftEntries(projectRoot, options) {
7674
6740
  }
7675
6741
  }
7676
6742
  try {
7677
- writeFileSync17(driftLogPath, JSON.stringify(entries, null, 2));
6743
+ writeFileSync15(driftLogPath, JSON.stringify(entries, null, 2));
7678
6744
  return { success: true };
7679
6745
  } catch (e) {
7680
6746
  return { success: false, error: `Could not write drift log: ${e.message}` };
@@ -7744,12 +6810,16 @@ ${BOLD7}Decantr telemetry${RESET13}`);
7744
6810
  DIM13 + "Run `decantr telemetry link` after login to attach these opaque IDs to your Decantr account/org." + RESET13
7745
6811
  );
7746
6812
  } else {
7747
- console.log(DIM13 + "Run `decantr init --telemetry` or `decantr telemetry link --enable` to opt in." + RESET13);
6813
+ console.log(
6814
+ DIM13 + "Run `decantr init --telemetry` or `decantr telemetry link --enable` to opt in." + RESET13
6815
+ );
7748
6816
  }
7749
6817
  }
7750
6818
  function printTelemetryExplain(projectRoot, options) {
7751
6819
  const status = getCliTelemetryIdentityStatus(projectRoot, { create: false });
7752
- const cliEvents = DECANTR_TELEMETRY_EVENT_CATALOG.filter((entry) => entry.allowedSources.includes("cli")).map((entry) => ({
6820
+ const cliEvents = DECANTR_TELEMETRY_EVENT_CATALOG.filter(
6821
+ (entry) => entry.allowedSources.includes("cli")
6822
+ ).map((entry) => ({
7753
6823
  name: entry.name,
7754
6824
  bucket: entry.bucket,
7755
6825
  privacy: entry.privacy,
@@ -7828,7 +6898,9 @@ async function linkTelemetryIdentity(projectRoot, options) {
7828
6898
  optIn(projectRoot);
7829
6899
  }
7830
6900
  if (!isOptedIn(projectRoot)) {
7831
- throw new Error("This project has not opted into telemetry. Re-run with --enable or use `decantr init --telemetry`.");
6901
+ throw new Error(
6902
+ "This project has not opted into telemetry. Re-run with --enable or use `decantr init --telemetry`."
6903
+ );
7832
6904
  }
7833
6905
  const identity = getCliTelemetryIdentityStatus(projectRoot, { create: true });
7834
6906
  if (!identity.installId && !identity.projectId) {
@@ -7836,9 +6908,13 @@ async function linkTelemetryIdentity(projectRoot, options) {
7836
6908
  }
7837
6909
  const apiKey = options.apiKey ?? getApiKeyOrToken();
7838
6910
  if (!apiKey) {
7839
- throw new Error("Run `decantr login --api-key=<key>` or pass `--api-key <key>` before linking telemetry.");
6911
+ throw new Error(
6912
+ "Run `decantr login --api-key=<key>` or pass `--api-key <key>` before linking telemetry."
6913
+ );
7840
6914
  }
7841
- const apiUrl = trimTrailingSlashes(options.apiUrl ?? process.env.DECANTR_API_URL ?? DEFAULT_API_URL);
6915
+ const apiUrl = trimTrailingSlashes(
6916
+ options.apiUrl ?? process.env.DECANTR_API_URL ?? DEFAULT_API_URL
6917
+ );
7842
6918
  const response = await fetch(`${apiUrl}/me/telemetry-link`, {
7843
6919
  method: "POST",
7844
6920
  headers: {
@@ -7862,7 +6938,9 @@ async function linkTelemetryIdentity(projectRoot, options) {
7862
6938
  console.log(` Install ID: ${identity.installId ?? `${DIM13}none${RESET13}`}`);
7863
6939
  console.log(` Project ID: ${identity.projectId ?? `${DIM13}none${RESET13}`}`);
7864
6940
  if (options.org) console.log(` Org: ${CYAN7}${options.org}${RESET13}`);
7865
- console.log(DIM13 + "These opaque IDs now attribute opted-in CLI usage to your Decantr account/org." + RESET13);
6941
+ console.log(
6942
+ DIM13 + "These opaque IDs now attribute opted-in CLI usage to your Decantr account/org." + RESET13
6943
+ );
7866
6944
  }
7867
6945
  function parseTelemetryOptions(args) {
7868
6946
  const options = {};
@@ -7899,9 +6977,9 @@ function trimTrailingSlashes(value) {
7899
6977
  }
7900
6978
 
7901
6979
  // src/commands/theme-switch.ts
7902
- import { existsSync as existsSync28, readFileSync as readFileSync21, writeFileSync as writeFileSync18 } from "fs";
7903
- import { join as join30 } from "path";
7904
- import { isV4 as isV48 } from "@decantr/essence-spec";
6980
+ import { existsSync as existsSync26, readFileSync as readFileSync19, writeFileSync as writeFileSync16 } from "fs";
6981
+ import { join as join28 } from "path";
6982
+ import { isV4 as isV47 } from "@decantr/essence-spec";
7905
6983
  var GREEN14 = "\x1B[32m";
7906
6984
  var RED11 = "\x1B[31m";
7907
6985
  var YELLOW10 = "\x1B[33m";
@@ -7917,21 +6995,21 @@ async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
7917
6995
  process.exitCode = 1;
7918
6996
  return;
7919
6997
  }
7920
- const essencePath = join30(projectRoot, "decantr.essence.json");
7921
- if (!existsSync28(essencePath)) {
6998
+ const essencePath = join28(projectRoot, "decantr.essence.json");
6999
+ if (!existsSync26(essencePath)) {
7922
7000
  console.error(`${RED11}No decantr.essence.json found. Run \`decantr init\` first.${RESET14}`);
7923
7001
  process.exitCode = 1;
7924
7002
  return;
7925
7003
  }
7926
7004
  let parsed;
7927
7005
  try {
7928
- parsed = JSON.parse(readFileSync21(essencePath, "utf-8"));
7006
+ parsed = JSON.parse(readFileSync19(essencePath, "utf-8"));
7929
7007
  } catch (e) {
7930
7008
  console.error(`${RED11}Could not read essence: ${e.message}${RESET14}`);
7931
7009
  process.exitCode = 1;
7932
7010
  return;
7933
7011
  }
7934
- if (!isV48(parsed)) {
7012
+ if (!isV47(parsed)) {
7935
7013
  console.error(
7936
7014
  `${RED11}Active workflows require Essence v4.0.0. Run \`decantr migrate --to v4\` first.${RESET14}`
7937
7015
  );
@@ -7976,7 +7054,7 @@ async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
7976
7054
  essence.dna.theme.mode = mode;
7977
7055
  }
7978
7056
  const registryClient = new RegistryClient({
7979
- cacheDir: join30(projectRoot, ".decantr", "cache")
7057
+ cacheDir: join28(projectRoot, ".decantr", "cache")
7980
7058
  });
7981
7059
  try {
7982
7060
  const themeResult = await registryClient.fetchTheme(themeName);
@@ -7991,7 +7069,7 @@ async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
7991
7069
  }
7992
7070
  } catch {
7993
7071
  }
7994
- writeFileSync18(essencePath, JSON.stringify(essence, null, 2) + "\n");
7072
+ writeFileSync16(essencePath, JSON.stringify(essence, null, 2) + "\n");
7995
7073
  console.log(`${GREEN14}Switched theme: ${oldThemeId} \u2192 ${themeName}${RESET14}`);
7996
7074
  if (shape) console.log(` ${DIM14}Shape: ${shape}${RESET14}`);
7997
7075
  if (mode) console.log(` ${DIM14}Mode: ${mode}${RESET14}`);
@@ -8299,8 +7377,8 @@ async function runSimplifiedInit(blueprints) {
8299
7377
  }
8300
7378
 
8301
7379
  // src/theme-commands.ts
8302
- import { existsSync as existsSync29, mkdirSync as mkdirSync16, readdirSync as readdirSync8, readFileSync as readFileSync22, rmSync as rmSync3, writeFileSync as writeFileSync19 } from "fs";
8303
- import { join as join31 } from "path";
7380
+ import { existsSync as existsSync27, mkdirSync as mkdirSync14, readdirSync as readdirSync7, readFileSync as readFileSync20, rmSync as rmSync3, writeFileSync as writeFileSync17 } from "fs";
7381
+ import { join as join29 } from "path";
8304
7382
  var REQUIRED_FIELDS = [
8305
7383
  "$schema",
8306
7384
  "id",
@@ -8360,20 +7438,20 @@ function validateCustomTheme(theme) {
8360
7438
  };
8361
7439
  }
8362
7440
  function createTheme(projectRoot, id, name) {
8363
- const customThemesDir = join31(projectRoot, ".decantr", "custom", "themes");
8364
- const themePath = join31(customThemesDir, `${id}.json`);
8365
- const howToPath = join31(customThemesDir, "how-to-theme.md");
8366
- mkdirSync16(customThemesDir, { recursive: true });
8367
- if (existsSync29(themePath)) {
7441
+ const customThemesDir = join29(projectRoot, ".decantr", "custom", "themes");
7442
+ const themePath = join29(customThemesDir, `${id}.json`);
7443
+ const howToPath = join29(customThemesDir, "how-to-theme.md");
7444
+ mkdirSync14(customThemesDir, { recursive: true });
7445
+ if (existsSync27(themePath)) {
8368
7446
  return {
8369
7447
  success: false,
8370
7448
  error: `Theme "${id}" already exists at ${themePath}`
8371
7449
  };
8372
7450
  }
8373
7451
  const skeleton = getThemeSkeleton(id, name);
8374
- writeFileSync19(themePath, JSON.stringify(skeleton, null, 2));
8375
- if (!existsSync29(howToPath)) {
8376
- writeFileSync19(howToPath, getHowToThemeDoc());
7452
+ writeFileSync17(themePath, JSON.stringify(skeleton, null, 2));
7453
+ if (!existsSync27(howToPath)) {
7454
+ writeFileSync17(howToPath, getHowToThemeDoc());
8377
7455
  }
8378
7456
  return {
8379
7457
  success: true,
@@ -8381,17 +7459,17 @@ function createTheme(projectRoot, id, name) {
8381
7459
  };
8382
7460
  }
8383
7461
  function listCustomThemes(projectRoot) {
8384
- const customThemesDir = join31(projectRoot, ".decantr", "custom", "themes");
8385
- if (!existsSync29(customThemesDir)) {
7462
+ const customThemesDir = join29(projectRoot, ".decantr", "custom", "themes");
7463
+ if (!existsSync27(customThemesDir)) {
8386
7464
  return [];
8387
7465
  }
8388
7466
  const themes = [];
8389
7467
  try {
8390
- const files = readdirSync8(customThemesDir).filter((f) => f.endsWith(".json"));
7468
+ const files = readdirSync7(customThemesDir).filter((f) => f.endsWith(".json"));
8391
7469
  for (const file of files) {
8392
- const filePath = join31(customThemesDir, file);
7470
+ const filePath = join29(customThemesDir, file);
8393
7471
  try {
8394
- const data = JSON.parse(readFileSync22(filePath, "utf-8"));
7472
+ const data = JSON.parse(readFileSync20(filePath, "utf-8"));
8395
7473
  themes.push({
8396
7474
  id: data.id || file.replace(".json", ""),
8397
7475
  name: data.name || data.id,
@@ -8406,8 +7484,8 @@ function listCustomThemes(projectRoot) {
8406
7484
  return themes;
8407
7485
  }
8408
7486
  function deleteTheme(projectRoot, id) {
8409
- const themePath = join31(projectRoot, ".decantr", "custom", "themes", `${id}.json`);
8410
- if (!existsSync29(themePath)) {
7487
+ const themePath = join29(projectRoot, ".decantr", "custom", "themes", `${id}.json`);
7488
+ if (!existsSync27(themePath)) {
8411
7489
  return {
8412
7490
  success: false,
8413
7491
  error: `Theme "${id}" not found at ${themePath}`
@@ -8424,7 +7502,7 @@ function deleteTheme(projectRoot, id) {
8424
7502
  }
8425
7503
  }
8426
7504
  function importTheme(projectRoot, sourcePath) {
8427
- if (!existsSync29(sourcePath)) {
7505
+ if (!existsSync27(sourcePath)) {
8428
7506
  return {
8429
7507
  success: false,
8430
7508
  errors: [`Source file not found: ${sourcePath}`]
@@ -8432,7 +7510,7 @@ function importTheme(projectRoot, sourcePath) {
8432
7510
  }
8433
7511
  let theme;
8434
7512
  try {
8435
- theme = JSON.parse(readFileSync22(sourcePath, "utf-8"));
7513
+ theme = JSON.parse(readFileSync20(sourcePath, "utf-8"));
8436
7514
  } catch (e) {
8437
7515
  return {
8438
7516
  success: false,
@@ -8448,14 +7526,14 @@ function importTheme(projectRoot, sourcePath) {
8448
7526
  }
8449
7527
  theme.source = "custom";
8450
7528
  const id = theme.id;
8451
- const customThemesDir = join31(projectRoot, ".decantr", "custom", "themes");
8452
- const destPath = join31(customThemesDir, `${id}.json`);
8453
- mkdirSync16(customThemesDir, { recursive: true });
8454
- const howToPath = join31(customThemesDir, "how-to-theme.md");
8455
- if (!existsSync29(howToPath)) {
8456
- writeFileSync19(howToPath, getHowToThemeDoc());
8457
- }
8458
- writeFileSync19(destPath, JSON.stringify(theme, null, 2));
7529
+ const customThemesDir = join29(projectRoot, ".decantr", "custom", "themes");
7530
+ const destPath = join29(customThemesDir, `${id}.json`);
7531
+ mkdirSync14(customThemesDir, { recursive: true });
7532
+ const howToPath = join29(customThemesDir, "how-to-theme.md");
7533
+ if (!existsSync27(howToPath)) {
7534
+ writeFileSync17(howToPath, getHowToThemeDoc());
7535
+ }
7536
+ writeFileSync17(destPath, JSON.stringify(theme, null, 2));
8459
7537
  return {
8460
7538
  success: true,
8461
7539
  path: destPath
@@ -9002,18 +8080,18 @@ function extractHostedAssetPaths(indexHtml) {
9002
8080
  return [...assetPaths];
9003
8081
  }
9004
8082
  function readHostedDistSnapshot(distPath) {
9005
- const resolvedDistPath = distPath ? resolveUserPath(distPath) : join32(process.cwd(), "dist");
9006
- const indexPath = join32(resolvedDistPath, "index.html");
9007
- if (!existsSync30(indexPath)) {
8083
+ const resolvedDistPath = distPath ? resolveUserPath(distPath) : join30(process.cwd(), "dist");
8084
+ const indexPath = join30(resolvedDistPath, "index.html");
8085
+ if (!existsSync28(indexPath)) {
9008
8086
  return void 0;
9009
8087
  }
9010
- const indexHtml = readFileSync23(indexPath, "utf-8");
8088
+ const indexHtml = readFileSync21(indexPath, "utf-8");
9011
8089
  const assetPaths = extractHostedAssetPaths(indexHtml);
9012
8090
  const assets = {};
9013
8091
  for (const assetPath of assetPaths) {
9014
- const assetFilePath = join32(resolvedDistPath, assetPath.replace(/^[/\\]+/, ""));
9015
- if (existsSync30(assetFilePath)) {
9016
- assets[assetPath] = readFileSync23(assetFilePath, "utf-8");
8092
+ const assetFilePath = join30(resolvedDistPath, assetPath.replace(/^[/\\]+/, ""));
8093
+ if (existsSync28(assetFilePath)) {
8094
+ assets[assetPath] = readFileSync21(assetFilePath, "utf-8");
9017
8095
  }
9018
8096
  }
9019
8097
  return {
@@ -9028,7 +8106,7 @@ function isHostedSourceSnapshotFile(path) {
9028
8106
  function readHostedSourceSnapshot(sourcePath) {
9029
8107
  if (!sourcePath) return void 0;
9030
8108
  const resolvedSourcePath = resolveUserPath(sourcePath);
9031
- if (!existsSync30(resolvedSourcePath)) {
8109
+ if (!existsSync28(resolvedSourcePath)) {
9032
8110
  return void 0;
9033
8111
  }
9034
8112
  const files = {};
@@ -9040,19 +8118,19 @@ function readHostedSourceSnapshot(sourcePath) {
9040
8118
  "build",
9041
8119
  "coverage"
9042
8120
  ]);
9043
- const rootPrefix = basename3(resolvedSourcePath);
8121
+ const rootPrefix = basename2(resolvedSourcePath);
9044
8122
  const walk = (absoluteDir, relativeDir) => {
9045
- for (const entry of readdirSync9(absoluteDir, { withFileTypes: true })) {
8123
+ for (const entry of readdirSync8(absoluteDir, { withFileTypes: true })) {
9046
8124
  if (ignoredDirNames.has(entry.name)) continue;
9047
- const absolutePath = join32(absoluteDir, entry.name);
9048
- const relativePath = join32(relativeDir, entry.name).replace(/\\/g, "/");
8125
+ const absolutePath = join30(absoluteDir, entry.name);
8126
+ const relativePath = join30(relativeDir, entry.name).replace(/\\/g, "/");
9049
8127
  if (entry.isDirectory()) {
9050
8128
  walk(absolutePath, relativePath);
9051
8129
  continue;
9052
8130
  }
9053
8131
  if (!entry.isFile()) continue;
9054
8132
  if (!isHostedSourceSnapshotFile(relativePath)) continue;
9055
- files[relativePath] = readFileSync23(absolutePath, "utf-8");
8133
+ files[relativePath] = readFileSync21(absolutePath, "utf-8");
9056
8134
  }
9057
8135
  };
9058
8136
  walk(resolvedSourcePath, rootPrefix);
@@ -9245,25 +8323,25 @@ async function printHostedExecutionPackBundle(essencePath, namespace, jsonOutput
9245
8323
  }
9246
8324
  async function compileHostedExecutionPackBundle(essencePath, namespace) {
9247
8325
  const client = getPublicAPIClient();
9248
- const resolvedPath = essencePath ? resolveUserPath(essencePath) : join32(process.cwd(), "decantr.essence.json");
9249
- if (!existsSync30(resolvedPath)) {
8326
+ const resolvedPath = essencePath ? resolveUserPath(essencePath) : join30(process.cwd(), "decantr.essence.json");
8327
+ if (!existsSync28(resolvedPath)) {
9250
8328
  throw new Error(`Essence file not found at ${resolvedPath}`);
9251
8329
  }
9252
- const essence = JSON.parse(readFileSync23(resolvedPath, "utf-8"));
8330
+ const essence = JSON.parse(readFileSync21(resolvedPath, "utf-8"));
9253
8331
  const bundle = await client.compileExecutionPacks(essence, namespace ? { namespace } : void 0);
9254
- const contextDir = join32(dirname6(resolvedPath), ".decantr", "context");
8332
+ const contextDir = join30(dirname6(resolvedPath), ".decantr", "context");
9255
8333
  return { resolvedPath, bundle, contextDir };
9256
8334
  }
9257
8335
  function writeHostedExecutionPackContextArtifacts(contextDir, bundle) {
9258
- mkdirSync17(contextDir, { recursive: true });
8336
+ mkdirSync15(contextDir, { recursive: true });
9259
8337
  return writeExecutionPackBundleArtifacts(contextDir, bundle);
9260
8338
  }
9261
8339
  function resolvePagePackIdForRoute(essencePath, route) {
9262
- if (!existsSync30(essencePath)) {
8340
+ if (!existsSync28(essencePath)) {
9263
8341
  throw new Error(`Essence file not found at ${essencePath}`);
9264
8342
  }
9265
- const essence = JSON.parse(readFileSync23(essencePath, "utf-8"));
9266
- if (!isV49(essence)) {
8343
+ const essence = JSON.parse(readFileSync21(essencePath, "utf-8"));
8344
+ if (!isV48(essence)) {
9267
8345
  throw new Error("Route-based pack resolution requires Essence v4.0.0.");
9268
8346
  }
9269
8347
  const target = essence.blueprint.routes?.[route];
@@ -9277,14 +8355,14 @@ function resolvePagePackIdForRoute(essencePath, route) {
9277
8355
  }
9278
8356
  async function printHostedSelectedExecutionPack(packType, id, essencePath, namespace, jsonOutput = false, writeContext = false) {
9279
8357
  const client = getPublicAPIClient();
9280
- const resolvedPath = essencePath ? resolveUserPath(essencePath) : join32(process.cwd(), "decantr.essence.json");
9281
- if (!existsSync30(resolvedPath)) {
8358
+ const resolvedPath = essencePath ? resolveUserPath(essencePath) : join30(process.cwd(), "decantr.essence.json");
8359
+ if (!existsSync28(resolvedPath)) {
9282
8360
  throw new Error(`Essence file not found at ${resolvedPath}`);
9283
8361
  }
9284
8362
  if ((packType === "section" || packType === "page" || packType === "mutation") && !id) {
9285
8363
  throw new Error(`Pack type "${packType}" requires an id.`);
9286
8364
  }
9287
- const essence = JSON.parse(readFileSync23(resolvedPath, "utf-8"));
8365
+ const essence = JSON.parse(readFileSync21(resolvedPath, "utf-8"));
9288
8366
  const selected = await client.selectExecutionPack(
9289
8367
  {
9290
8368
  essence,
@@ -9295,17 +8373,17 @@ async function printHostedSelectedExecutionPack(packType, id, essencePath, names
9295
8373
  );
9296
8374
  let writtenContextDir = null;
9297
8375
  if (writeContext) {
9298
- const contextDir = join32(dirname6(resolvedPath), ".decantr", "context");
9299
- mkdirSync17(contextDir, { recursive: true });
9300
- writeFileSync20(
9301
- join32(contextDir, "pack-manifest.json"),
8376
+ const contextDir = join30(dirname6(resolvedPath), ".decantr", "context");
8377
+ mkdirSync15(contextDir, { recursive: true });
8378
+ writeFileSync18(
8379
+ join30(contextDir, "pack-manifest.json"),
9302
8380
  JSON.stringify(selected.manifest, null, 2) + "\n"
9303
8381
  );
9304
8382
  const manifestEntry = selected.selector.packType === "scaffold" ? selected.manifest.scaffold : selected.selector.packType === "review" ? selected.manifest.review : selected.selector.packType === "section" ? selected.manifest.sections.find((entry) => entry.id === selected.selector.id) : selected.selector.packType === "page" ? selected.manifest.pages.find((entry) => entry.id === selected.selector.id) : selected.manifest.mutations.find((entry) => entry.id === selected.selector.id);
9305
8383
  const markdownFile = manifestEntry?.markdown ?? `${selected.selector.packType}${selected.selector.id ? `-${selected.selector.id}` : ""}-pack.md`;
9306
8384
  const jsonFile = manifestEntry?.json ?? `${selected.selector.packType}${selected.selector.id ? `-${selected.selector.id}` : ""}-pack.json`;
9307
- writeFileSync20(join32(contextDir, markdownFile), selected.pack.renderedMarkdown);
9308
- writeFileSync20(join32(contextDir, jsonFile), JSON.stringify(selected.pack, null, 2) + "\n");
8385
+ writeFileSync18(join30(contextDir, markdownFile), selected.pack.renderedMarkdown);
8386
+ writeFileSync18(join30(contextDir, jsonFile), JSON.stringify(selected.pack, null, 2) + "\n");
9309
8387
  writtenContextDir = contextDir;
9310
8388
  }
9311
8389
  if (jsonOutput) {
@@ -9330,20 +8408,20 @@ async function printHostedSelectedExecutionPack(packType, id, essencePath, names
9330
8408
  }
9331
8409
  async function printHostedExecutionPackManifest(essencePath, namespace, jsonOutput = false, writeContext = false) {
9332
8410
  const client = getPublicAPIClient();
9333
- const resolvedPath = essencePath ? resolveUserPath(essencePath) : join32(process.cwd(), "decantr.essence.json");
9334
- if (!existsSync30(resolvedPath)) {
8411
+ const resolvedPath = essencePath ? resolveUserPath(essencePath) : join30(process.cwd(), "decantr.essence.json");
8412
+ if (!existsSync28(resolvedPath)) {
9335
8413
  throw new Error(`Essence file not found at ${resolvedPath}`);
9336
8414
  }
9337
- const essence = JSON.parse(readFileSync23(resolvedPath, "utf-8"));
8415
+ const essence = JSON.parse(readFileSync21(resolvedPath, "utf-8"));
9338
8416
  const manifest = await client.getExecutionPackManifest(
9339
8417
  essence,
9340
8418
  namespace ? { namespace } : void 0
9341
8419
  );
9342
8420
  let writtenContextDir = null;
9343
8421
  if (writeContext) {
9344
- const contextDir = join32(dirname6(resolvedPath), ".decantr", "context");
9345
- mkdirSync17(contextDir, { recursive: true });
9346
- writeFileSync20(join32(contextDir, "pack-manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
8422
+ const contextDir = join30(dirname6(resolvedPath), ".decantr", "context");
8423
+ mkdirSync15(contextDir, { recursive: true });
8424
+ writeFileSync18(join30(contextDir, "pack-manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
9347
8425
  writtenContextDir = contextDir;
9348
8426
  }
9349
8427
  if (jsonOutput) {
@@ -9364,14 +8442,14 @@ async function printHostedExecutionPackManifest(essencePath, namespace, jsonOutp
9364
8442
  }
9365
8443
  }
9366
8444
  async function hydrateHostedExecutionPacksIfMissing(projectRoot, namespace = "@official") {
9367
- const contextDir = join32(projectRoot, ".decantr", "context");
9368
- const reviewPackPath = join32(contextDir, "review-pack.json");
9369
- const manifestPath = join32(contextDir, "pack-manifest.json");
9370
- if (existsSync30(reviewPackPath) && existsSync30(manifestPath)) {
8445
+ const contextDir = join30(projectRoot, ".decantr", "context");
8446
+ const reviewPackPath = join30(contextDir, "review-pack.json");
8447
+ const manifestPath = join30(contextDir, "pack-manifest.json");
8448
+ if (existsSync28(reviewPackPath) && existsSync28(manifestPath)) {
9371
8449
  return { attempted: false, hydrated: false };
9372
8450
  }
9373
- const essencePath = join32(projectRoot, "decantr.essence.json");
9374
- if (!existsSync30(essencePath)) {
8451
+ const essencePath = join30(projectRoot, "decantr.essence.json");
8452
+ if (!existsSync28(essencePath)) {
9375
8453
  return { attempted: false, hydrated: false };
9376
8454
  }
9377
8455
  const reviewHydration = await hydrateHostedReviewPackIfMissing(projectRoot, namespace);
@@ -9380,9 +8458,9 @@ async function hydrateHostedExecutionPacksIfMissing(projectRoot, namespace = "@o
9380
8458
  }
9381
8459
  try {
9382
8460
  const client = getPublicAPIClient();
9383
- const essence = JSON.parse(readFileSync23(essencePath, "utf-8"));
8461
+ const essence = JSON.parse(readFileSync21(essencePath, "utf-8"));
9384
8462
  const bundle = await client.compileExecutionPacks(essence, { namespace });
9385
- mkdirSync17(contextDir, { recursive: true });
8463
+ mkdirSync15(contextDir, { recursive: true });
9386
8464
  writeExecutionPackBundleArtifacts(contextDir, bundle);
9387
8465
  return { attempted: true, hydrated: true, scope: "bundle" };
9388
8466
  } catch {
@@ -9390,19 +8468,19 @@ async function hydrateHostedExecutionPacksIfMissing(projectRoot, namespace = "@o
9390
8468
  }
9391
8469
  }
9392
8470
  async function hydrateHostedReviewPackIfMissing(projectRoot, namespace = "@official") {
9393
- const contextDir = join32(projectRoot, ".decantr", "context");
9394
- const reviewPackPath = join32(contextDir, "review-pack.json");
9395
- const manifestPath = join32(contextDir, "pack-manifest.json");
9396
- if (existsSync30(reviewPackPath) && existsSync30(manifestPath)) {
8471
+ const contextDir = join30(projectRoot, ".decantr", "context");
8472
+ const reviewPackPath = join30(contextDir, "review-pack.json");
8473
+ const manifestPath = join30(contextDir, "pack-manifest.json");
8474
+ if (existsSync28(reviewPackPath) && existsSync28(manifestPath)) {
9397
8475
  return { attempted: false, hydrated: false };
9398
8476
  }
9399
- const essencePath = join32(projectRoot, "decantr.essence.json");
9400
- if (!existsSync30(essencePath)) {
8477
+ const essencePath = join30(projectRoot, "decantr.essence.json");
8478
+ if (!existsSync28(essencePath)) {
9401
8479
  return { attempted: false, hydrated: false };
9402
8480
  }
9403
8481
  try {
9404
8482
  const client = getPublicAPIClient();
9405
- const essence = JSON.parse(readFileSync23(essencePath, "utf-8"));
8483
+ const essence = JSON.parse(readFileSync21(essencePath, "utf-8"));
9406
8484
  const selected = await client.selectExecutionPack(
9407
8485
  {
9408
8486
  essence,
@@ -9410,14 +8488,14 @@ async function hydrateHostedReviewPackIfMissing(projectRoot, namespace = "@offic
9410
8488
  },
9411
8489
  { namespace }
9412
8490
  );
9413
- mkdirSync17(contextDir, { recursive: true });
9414
- writeFileSync20(join32(contextDir, "review-pack.md"), selected.pack.renderedMarkdown);
9415
- writeFileSync20(
9416
- join32(contextDir, "review-pack.json"),
8491
+ mkdirSync15(contextDir, { recursive: true });
8492
+ writeFileSync18(join30(contextDir, "review-pack.md"), selected.pack.renderedMarkdown);
8493
+ writeFileSync18(
8494
+ join30(contextDir, "review-pack.json"),
9417
8495
  JSON.stringify(selected.pack, null, 2) + "\n"
9418
8496
  );
9419
- if (!existsSync30(manifestPath)) {
9420
- writeFileSync20(manifestPath, JSON.stringify(selected.manifest, null, 2) + "\n");
8497
+ if (!existsSync28(manifestPath)) {
8498
+ writeFileSync18(manifestPath, JSON.stringify(selected.manifest, null, 2) + "\n");
9421
8499
  }
9422
8500
  return { attempted: true, hydrated: true, scope: "review" };
9423
8501
  } catch {
@@ -9427,17 +8505,17 @@ async function hydrateHostedReviewPackIfMissing(projectRoot, namespace = "@offic
9427
8505
  async function printHostedFileCritique(sourcePath, namespace, jsonOutput = false, essencePath, treatmentsPath) {
9428
8506
  const client = getPublicAPIClient();
9429
8507
  const resolvedSourcePath = resolveUserPath(sourcePath);
9430
- const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) : join32(process.cwd(), "decantr.essence.json");
9431
- const resolvedTreatmentsPath = treatmentsPath ? resolveUserPath(treatmentsPath) : join32(process.cwd(), "src", "styles", "treatments.css");
9432
- if (!existsSync30(resolvedSourcePath)) {
8508
+ const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) : join30(process.cwd(), "decantr.essence.json");
8509
+ const resolvedTreatmentsPath = treatmentsPath ? resolveUserPath(treatmentsPath) : join30(process.cwd(), "src", "styles", "treatments.css");
8510
+ if (!existsSync28(resolvedSourcePath)) {
9433
8511
  throw new Error(`Source file not found at ${resolvedSourcePath}`);
9434
8512
  }
9435
- if (!existsSync30(resolvedEssencePath)) {
8513
+ if (!existsSync28(resolvedEssencePath)) {
9436
8514
  throw new Error(`Essence file not found at ${resolvedEssencePath}`);
9437
8515
  }
9438
- const code = readFileSync23(resolvedSourcePath, "utf-8");
9439
- const essence = JSON.parse(readFileSync23(resolvedEssencePath, "utf-8"));
9440
- const treatmentsCss = existsSync30(resolvedTreatmentsPath) ? readFileSync23(resolvedTreatmentsPath, "utf-8") : void 0;
8516
+ const code = readFileSync21(resolvedSourcePath, "utf-8");
8517
+ const essence = JSON.parse(readFileSync21(resolvedEssencePath, "utf-8"));
8518
+ const treatmentsCss = existsSync28(resolvedTreatmentsPath) ? readFileSync21(resolvedTreatmentsPath, "utf-8") : void 0;
9441
8519
  const report = await client.critiqueFile(
9442
8520
  {
9443
8521
  essence,
@@ -9461,11 +8539,11 @@ async function printHostedFileCritique(sourcePath, namespace, jsonOutput = false
9461
8539
  }
9462
8540
  async function printHostedProjectAudit(namespace, jsonOutput = false, essencePath, distPath, sourcesPath) {
9463
8541
  const client = getPublicAPIClient();
9464
- const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) : join32(process.cwd(), "decantr.essence.json");
9465
- if (!existsSync30(resolvedEssencePath)) {
8542
+ const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) : join30(process.cwd(), "decantr.essence.json");
8543
+ if (!existsSync28(resolvedEssencePath)) {
9466
8544
  throw new Error(`Essence file not found at ${resolvedEssencePath}`);
9467
8545
  }
9468
- const essence = JSON.parse(readFileSync23(resolvedEssencePath, "utf-8"));
8546
+ const essence = JSON.parse(readFileSync21(resolvedEssencePath, "utf-8"));
9469
8547
  const dist = readHostedDistSnapshot(distPath);
9470
8548
  const sources = readHostedSourceSnapshot(sourcesPath);
9471
8549
  const report = await client.auditProject(
@@ -9483,7 +8561,7 @@ async function printHostedProjectAudit(namespace, jsonOutput = false, essencePat
9483
8561
  console.log(heading2("Hosted Project Audit"));
9484
8562
  console.log(` Essence: ${resolvedEssencePath}`);
9485
8563
  console.log(
9486
- ` Dist snapshot: ${dist ? distPath ? resolveUserPath(distPath) : join32(process.cwd(), "dist") : "none"}`
8564
+ ` Dist snapshot: ${dist ? distPath ? resolveUserPath(distPath) : join30(process.cwd(), "dist") : "none"}`
9487
8565
  );
9488
8566
  console.log(
9489
8567
  ` Source snapshot: ${sources && sourcesPath ? resolveUserPath(sourcesPath) : "none"}`
@@ -9605,21 +8683,21 @@ function findPatternCandidateBySlug(candidates, slug) {
9605
8683
  function readSuggestCodeContext(projectRoot, route, file) {
9606
8684
  const pieces = [];
9607
8685
  if (file) {
9608
- const resolved = isAbsolute3(file) ? file : join32(projectRoot, file);
9609
- if (existsSync30(resolved)) {
9610
- pieces.push(readFileSync23(resolved, "utf-8"));
8686
+ const resolved = isAbsolute3(file) ? file : join30(projectRoot, file);
8687
+ if (existsSync28(resolved)) {
8688
+ pieces.push(readFileSync21(resolved, "utf-8"));
9611
8689
  }
9612
8690
  }
9613
8691
  if (route) {
9614
- const analysisPath = join32(projectRoot, ".decantr", "analysis.json");
9615
- if (existsSync30(analysisPath)) {
8692
+ const analysisPath = join30(projectRoot, ".decantr", "analysis.json");
8693
+ if (existsSync28(analysisPath)) {
9616
8694
  try {
9617
- const analysis = JSON.parse(readFileSync23(analysisPath, "utf-8"));
8695
+ const analysis = JSON.parse(readFileSync21(analysisPath, "utf-8"));
9618
8696
  const routeEntry = analysis.routes?.routes?.find((entry) => entry.path === route);
9619
8697
  if (routeEntry?.file) {
9620
- const resolved = join32(projectRoot, routeEntry.file);
9621
- if (existsSync30(resolved)) {
9622
- pieces.push(readFileSync23(resolved, "utf-8"));
8698
+ const resolved = join30(projectRoot, routeEntry.file);
8699
+ if (existsSync28(resolved)) {
8700
+ pieces.push(readFileSync21(resolved, "utf-8"));
9623
8701
  }
9624
8702
  }
9625
8703
  } catch {
@@ -9632,7 +8710,9 @@ function localPatternMatches(projectRoot, query) {
9632
8710
  if (!projectRoot) return [];
9633
8711
  const pack = readLocalPatternPack(projectRoot);
9634
8712
  const patterns = Array.isArray(pack?.patterns) ? pack.patterns : [];
9635
- const queryTerms = query.toLowerCase().split(/[^a-z0-9]+/).filter((term) => term.length > 1).flatMap((term) => term.endsWith("s") && term.length > 3 ? [term, term.slice(0, -1)] : [term]);
8713
+ const queryTerms = query.toLowerCase().split(/[^a-z0-9]+/).filter((term) => term.length > 1).flatMap(
8714
+ (term) => term.endsWith("s") && term.length > 3 ? [term, term.slice(0, -1)] : [term]
8715
+ );
9636
8716
  if (queryTerms.length === 0) return [];
9637
8717
  return patterns.map((pattern) => {
9638
8718
  const id = typeof pattern.id === "string" ? pattern.id : "local-pattern";
@@ -9703,7 +8783,7 @@ async function cmdSuggest(query, options = {}) {
9703
8783
  }
9704
8784
  }
9705
8785
  const registryClient = new RegistryClient({
9706
- cacheDir: join32(projectRoot, ".decantr", "cache")
8786
+ cacheDir: join30(projectRoot, ".decantr", "cache")
9707
8787
  });
9708
8788
  const code = options.fromCode || options.file ? readSuggestCodeContext(projectRoot, options.route, options.file) : "";
9709
8789
  const localMatches = localPatternMatches(projectRoot, [query, code].filter(Boolean).join("\n"));
@@ -9758,7 +8838,9 @@ async function cmdSuggest(query, options = {}) {
9758
8838
  if (matches.length === 0) {
9759
8839
  console.log(dim3("No hosted/bundled registry patterns matched this query."));
9760
8840
  console.log("");
9761
- console.log(dim3('Use local law first, or run "decantr list patterns" to browse registry options.'));
8841
+ console.log(
8842
+ dim3('Use local law first, or run "decantr list patterns" to browse registry options.')
8843
+ );
9762
8844
  return;
9763
8845
  }
9764
8846
  for (const match of matches.slice(0, 8)) {
@@ -9790,7 +8872,7 @@ async function cmdGet(type, id) {
9790
8872
  }
9791
8873
  const apiType = CONTENT_TYPE_TO_API_CONTENT_TYPE3[type];
9792
8874
  const registryClient = new RegistryClient({
9793
- cacheDir: join32(process.cwd(), ".decantr", "cache")
8875
+ cacheDir: join30(process.cwd(), ".decantr", "cache")
9794
8876
  });
9795
8877
  const result = await registryClient.fetchContentItem(apiType, id);
9796
8878
  if (result) {
@@ -9807,10 +8889,10 @@ async function cmdGet(type, id) {
9807
8889
  return;
9808
8890
  }
9809
8891
  async function cmdValidate(path) {
9810
- const essencePath = path || join32(process.cwd(), "decantr.essence.json");
8892
+ const essencePath = path || join30(process.cwd(), "decantr.essence.json");
9811
8893
  let raw;
9812
8894
  try {
9813
- raw = readFileSync23(essencePath, "utf-8");
8895
+ raw = readFileSync21(essencePath, "utf-8");
9814
8896
  } catch {
9815
8897
  console.error(error2(`Could not read ${essencePath}`));
9816
8898
  process.exitCode = 1;
@@ -9824,7 +8906,7 @@ async function cmdValidate(path) {
9824
8906
  process.exitCode = 1;
9825
8907
  return;
9826
8908
  }
9827
- const detectedVersion = isV49(essence) ? "v4" : "legacy";
8909
+ const detectedVersion = isV48(essence) ? "v4" : "legacy";
9828
8910
  console.log(`${DIM16}Detected essence version: ${detectedVersion}${RESET16}`);
9829
8911
  const result = validateEssence2(essence);
9830
8912
  if (result.valid) {
@@ -9877,7 +8959,7 @@ async function cmdList(type, sort, recommended, intelligenceSource, blueprintSet
9877
8959
  return;
9878
8960
  }
9879
8961
  const registryClient = new RegistryClient({
9880
- cacheDir: join32(process.cwd(), ".decantr", "cache")
8962
+ cacheDir: join30(process.cwd(), ".decantr", "cache")
9881
8963
  });
9882
8964
  const result = await registryClient.fetchContentList(
9883
8965
  type,
@@ -9960,10 +9042,10 @@ ${CYAN9}Telemetry enabled.${RESET16} Decantr will send privacy-filtered CLI prod
9960
9042
  }
9961
9043
  function readCliPackageVersion2() {
9962
9044
  const here = dirname6(fileURLToPath3(import.meta.url));
9963
- const candidates = [join32(here, "..", "package.json"), join32(here, "..", "..", "package.json")];
9045
+ const candidates = [join30(here, "..", "package.json"), join30(here, "..", "..", "package.json")];
9964
9046
  for (const candidate of candidates) {
9965
9047
  try {
9966
- const pkg = JSON.parse(readFileSync23(candidate, "utf-8"));
9048
+ const pkg = JSON.parse(readFileSync21(candidate, "utf-8"));
9967
9049
  if (pkg.version) return pkg.version;
9968
9050
  } catch {
9969
9051
  }
@@ -9974,19 +9056,19 @@ function timestampForFile() {
9974
9056
  return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
9975
9057
  }
9976
9058
  function backupExistingEssence(projectRoot, label) {
9977
- const essencePath = join32(projectRoot, "decantr.essence.json");
9978
- if (!existsSync30(essencePath)) return null;
9979
- const backupPath = join32(
9059
+ const essencePath = join30(projectRoot, "decantr.essence.json");
9060
+ if (!existsSync28(essencePath)) return null;
9061
+ const backupPath = join30(
9980
9062
  projectRoot,
9981
9063
  `decantr.essence.${label}.${timestampForFile()}.backup.json`
9982
9064
  );
9983
- writeFileSync20(backupPath, readFileSync23(essencePath, "utf-8"), "utf-8");
9065
+ writeFileSync18(backupPath, readFileSync21(essencePath, "utf-8"), "utf-8");
9984
9066
  return backupPath;
9985
9067
  }
9986
9068
  function writeBrownfieldProjectJson(input) {
9987
- const decantrDir = join32(input.projectRoot, ".decantr");
9988
- mkdirSync17(join32(decantrDir, "context"), { recursive: true });
9989
- mkdirSync17(join32(decantrDir, "cache"), { recursive: true });
9069
+ const decantrDir = join30(input.projectRoot, ".decantr");
9070
+ mkdirSync15(join30(decantrDir, "context"), { recursive: true });
9071
+ mkdirSync15(join30(decantrDir, "cache"), { recursive: true });
9990
9072
  const now = (/* @__PURE__ */ new Date()).toISOString();
9991
9073
  const projectJson = {
9992
9074
  detected: {
@@ -10027,7 +9109,7 @@ function writeBrownfieldProjectJson(input) {
10027
9109
  }
10028
9110
  }
10029
9111
  };
10030
- writeFileSync20(join32(decantrDir, "project.json"), JSON.stringify(projectJson, null, 2) + "\n");
9112
+ writeFileSync18(join30(decantrDir, "project.json"), JSON.stringify(projectJson, null, 2) + "\n");
10031
9113
  }
10032
9114
  async function applyAcceptedBrownfieldProposal(input) {
10033
9115
  const proposal = readBrownfieldProposal(input.projectRoot);
@@ -10043,8 +9125,8 @@ async function applyAcceptedBrownfieldProposal(input) {
10043
9125
  process.exitCode = 1;
10044
9126
  return;
10045
9127
  }
10046
- const essencePath = join32(input.projectRoot, "decantr.essence.json");
10047
- const hasEssence = existsSync30(essencePath);
9128
+ const essencePath = join30(input.projectRoot, "decantr.essence.json");
9129
+ const hasEssence = existsSync28(essencePath);
10048
9130
  let essence;
10049
9131
  let backupPath = null;
10050
9132
  if (input.mode === "accept" && hasEssence) {
@@ -10058,8 +9140,8 @@ async function applyAcceptedBrownfieldProposal(input) {
10058
9140
  return;
10059
9141
  }
10060
9142
  if (input.mode === "merge" && hasEssence) {
10061
- const existing = JSON.parse(readFileSync23(essencePath, "utf-8"));
10062
- if (!isV49(existing)) {
9143
+ const existing = JSON.parse(readFileSync21(essencePath, "utf-8"));
9144
+ if (!isV48(existing)) {
10063
9145
  console.log(
10064
9146
  error2(
10065
9147
  "Existing essence is not v4. Run `decantr migrate --to v4` before merging a brownfield proposal."
@@ -10095,9 +9177,9 @@ async function applyAcceptedBrownfieldProposal(input) {
10095
9177
  assistantBridge: input.assistantBridge,
10096
9178
  mode: input.mode
10097
9179
  });
10098
- writeFileSync20(essencePath, JSON.stringify(essence, null, 2) + "\n", "utf-8");
9180
+ writeFileSync18(essencePath, JSON.stringify(essence, null, 2) + "\n", "utf-8");
10099
9181
  const registryClient = new RegistryClient({
10100
- cacheDir: join32(input.projectRoot, ".decantr", "cache"),
9182
+ cacheDir: join30(input.projectRoot, ".decantr", "cache"),
10101
9183
  offline: true,
10102
9184
  projectRoot: input.projectRoot
10103
9185
  });
@@ -10124,7 +9206,7 @@ async function applyAcceptedBrownfieldProposal(input) {
10124
9206
  }
10125
9207
  const appliedRuleFiles = input.assistantBridge === "apply" ? applyAssistantBridge(input.projectRoot, input.detected) : [];
10126
9208
  console.log(success3("\nBrownfield proposal accepted.\n"));
10127
- const projectLabel = input.workspaceInfo.appRoot !== input.workspaceInfo.workspaceRoot ? relative7(input.workspaceInfo.workspaceRoot, input.workspaceInfo.appRoot).replace(/\\/g, "/") : void 0;
9209
+ const projectLabel = input.workspaceInfo.appRoot !== input.workspaceInfo.workspaceRoot ? relative6(input.workspaceInfo.workspaceRoot, input.workspaceInfo.appRoot).replace(/\\/g, "/") : void 0;
10128
9210
  console.log(" Files created/updated:");
10129
9211
  console.log(
10130
9212
  ` ${cyan3(displayProjectPath(input.workspaceInfo, "decantr.essence.json"))} Observed brownfield contract`
@@ -10276,7 +9358,7 @@ async function cmdInit(args) {
10276
9358
  }
10277
9359
  }
10278
9360
  const registryClient = new RegistryClient({
10279
- cacheDir: join32(projectRoot, ".decantr", "cache"),
9361
+ cacheDir: join30(projectRoot, ".decantr", "cache"),
10280
9362
  apiUrl: args.registry,
10281
9363
  offline: args.offline,
10282
9364
  projectRoot
@@ -10608,7 +9690,7 @@ ${YELLOW12}You're offline. Scaffolding Decantr default.${RESET16}`);
10608
9690
  if (appliedRuleFiles.length > 0) {
10609
9691
  console.log(` ${dim3(`Rule bridge applied: ${appliedRuleFiles.join(", ")}`)}`);
10610
9692
  }
10611
- if (!existsSync30(join32(projectRoot, "package.json"))) {
9693
+ if (!existsSync28(join30(projectRoot, "package.json"))) {
10612
9694
  console.log("");
10613
9695
  console.log(
10614
9696
  dim3(` Note: ${cyan3("decantr init")} created Decantr contract/context files only.`)
@@ -10619,7 +9701,7 @@ ${YELLOW12}You're offline. Scaffolding Decantr default.${RESET16}`);
10619
9701
  )
10620
9702
  );
10621
9703
  }
10622
- const hasCompiledPacks = existsSync30(join32(projectRoot, ".decantr", "context", "scaffold-pack.md"));
9704
+ const hasCompiledPacks = existsSync28(join30(projectRoot, ".decantr", "context", "scaffold-pack.md"));
10623
9705
  console.log("");
10624
9706
  console.log(" Next steps:");
10625
9707
  if (hasCompiledPacks) {
@@ -10659,7 +9741,7 @@ ${YELLOW12}You're offline. Scaffolding Decantr default.${RESET16}`);
10659
9741
  console.log(` ${cyan3("decantr upgrade")} Update to latest patterns`);
10660
9742
  console.log(` ${cyan3("decantr check")} Detect drift issues`);
10661
9743
  console.log(` ${cyan3("decantr migrate --to v4")} Migrate older essence files to v4`);
10662
- const essenceContent = readFileSync23(result.essencePath, "utf-8");
9744
+ const essenceContent = readFileSync21(result.essencePath, "utf-8");
10663
9745
  const essence = JSON.parse(essenceContent);
10664
9746
  const validation = validateEssence2(essence);
10665
9747
  if (!validation.valid) {
@@ -10668,7 +9750,7 @@ Validation warnings: ${validation.errors.join(", ")}`));
10668
9750
  }
10669
9751
  console.log("");
10670
9752
  let promptPages;
10671
- if (isV49(essence)) {
9753
+ if (isV48(essence)) {
10672
9754
  const allPages = essence.blueprint.sections.flatMap(
10673
9755
  (s) => s.pages.map((p) => ({ ...p, _sectionId: s.id, _shell: s.shell }))
10674
9756
  );
@@ -10714,25 +9796,25 @@ Validation warnings: ${validation.errors.join(", ")}`));
10714
9796
  }
10715
9797
  }
10716
9798
  async function cmdStatus(projectRoot = process.cwd()) {
10717
- const essencePath = join32(projectRoot, "decantr.essence.json");
10718
- const projectJsonPath = join32(projectRoot, ".decantr", "project.json");
9799
+ const essencePath = join30(projectRoot, "decantr.essence.json");
9800
+ const projectJsonPath = join30(projectRoot, ".decantr", "project.json");
10719
9801
  console.log(heading2("Decantr Project Status"));
10720
- if (!existsSync30(essencePath)) {
9802
+ if (!existsSync28(essencePath)) {
10721
9803
  console.log(`${RED12}No decantr.essence.json found.${RESET16}`);
10722
9804
  console.log(dim3('Run "decantr init" to create one.'));
10723
9805
  return;
10724
9806
  }
10725
9807
  try {
10726
- const essence = JSON.parse(readFileSync23(essencePath, "utf-8"));
9808
+ const essence = JSON.parse(readFileSync21(essencePath, "utf-8"));
10727
9809
  const validation = validateEssence2(essence);
10728
- const essenceVersion = isV49(essence) ? "v4" : "legacy";
9810
+ const essenceVersion = isV48(essence) ? "v4" : "legacy";
10729
9811
  console.log(`${BOLD9}Essence:${RESET16}`);
10730
9812
  if (validation.valid) {
10731
9813
  console.log(` ${GREEN16}Valid${RESET16} (${essenceVersion})`);
10732
9814
  } else {
10733
9815
  console.log(` ${RED12}Invalid: ${validation.errors.join(", ")}${RESET16}`);
10734
9816
  }
10735
- if (isV49(essence)) {
9817
+ if (isV48(essence)) {
10736
9818
  const v4 = essence;
10737
9819
  const sections = v4.blueprint.sections;
10738
9820
  const flatPages = sections.flatMap((section) => section.pages ?? []);
@@ -10771,9 +9853,9 @@ async function cmdStatus(projectRoot = process.cwd()) {
10771
9853
  }
10772
9854
  console.log("");
10773
9855
  console.log(`${BOLD9}Sync Status:${RESET16}`);
10774
- if (existsSync30(projectJsonPath)) {
9856
+ if (existsSync28(projectJsonPath)) {
10775
9857
  try {
10776
- const projectJson = JSON.parse(readFileSync23(projectJsonPath, "utf-8"));
9858
+ const projectJson = JSON.parse(readFileSync21(projectJsonPath, "utf-8"));
10777
9859
  const syncStatus = projectJson.sync?.status || "unknown";
10778
9860
  const lastSync = projectJson.sync?.lastSync || "never";
10779
9861
  const source = projectJson.sync?.registrySource || "unknown";
@@ -10791,7 +9873,7 @@ async function cmdStatus(projectRoot = process.cwd()) {
10791
9873
  }
10792
9874
  async function cmdSync() {
10793
9875
  const projectRoot = process.cwd();
10794
- const cacheDir = join32(projectRoot, ".decantr", "cache");
9876
+ const cacheDir = join30(projectRoot, ".decantr", "cache");
10795
9877
  console.log(heading2("Syncing registry content..."));
10796
9878
  const result = await syncRegistry(cacheDir);
10797
9879
  if (result.synced.length > 0) {
@@ -10985,14 +10067,14 @@ ${BOLD9}Examples:${RESET16}
10985
10067
  process.exitCode = 1;
10986
10068
  return;
10987
10069
  }
10988
- const themePath = join32(projectRoot, ".decantr", "custom", "themes", `${name}.json`);
10989
- if (!existsSync30(themePath)) {
10070
+ const themePath = join30(projectRoot, ".decantr", "custom", "themes", `${name}.json`);
10071
+ if (!existsSync28(themePath)) {
10990
10072
  console.error(error2(`Theme "${name}" not found at ${themePath}`));
10991
10073
  process.exitCode = 1;
10992
10074
  return;
10993
10075
  }
10994
10076
  try {
10995
- const theme = JSON.parse(readFileSync23(themePath, "utf-8"));
10077
+ const theme = JSON.parse(readFileSync21(themePath, "utf-8"));
10996
10078
  const result = validateCustomTheme(theme);
10997
10079
  if (result.valid) {
10998
10080
  console.log(success3(`Custom theme "${name}" is valid`));
@@ -11124,13 +10206,42 @@ function withProject(command, projectArg) {
11124
10206
  return projectArg ? `${command} --project ${projectArg}` : command;
11125
10207
  }
11126
10208
  function displayProjectPath(workspaceInfo, projectPath) {
11127
- const absolutePath = join32(workspaceInfo.appRoot, projectPath);
11128
- const relativePath = relative7(workspaceInfo.cwd, absolutePath).replace(/\\/g, "/");
10209
+ const absolutePath = join30(workspaceInfo.appRoot, projectPath);
10210
+ const relativePath = relative6(workspaceInfo.cwd, absolutePath).replace(/\\/g, "/");
11129
10211
  if (relativePath && !relativePath.startsWith("..") && !isAbsolute3(relativePath)) {
11130
10212
  return relativePath;
11131
10213
  }
11132
10214
  return absolutePath;
11133
10215
  }
10216
+ function projectRelativeGraphPath(projectRoot, filePath) {
10217
+ const relativePath = relative6(projectRoot, isAbsolute3(filePath) ? filePath : resolve4(filePath));
10218
+ if (relativePath && !relativePath.startsWith("..") && !isAbsolute3(relativePath)) {
10219
+ return relativePath.replace(/\\/g, "/");
10220
+ }
10221
+ return null;
10222
+ }
10223
+ function graphSourceNodeIdForTaskFile(projectRoot, snapshot, filePath) {
10224
+ const trimmed = filePath.trim();
10225
+ if (!trimmed) return null;
10226
+ if (trimmed.startsWith("src:") && snapshot.nodes.some((node) => node.id === trimmed)) {
10227
+ return trimmed;
10228
+ }
10229
+ const candidates = /* @__PURE__ */ new Set();
10230
+ candidates.add(trimmed.replace(/\\/g, "/").replace(/^\.\//, ""));
10231
+ const cwdRelative = projectRelativeGraphPath(projectRoot, trimmed);
10232
+ if (cwdRelative) candidates.add(cwdRelative);
10233
+ const projectRelative = projectRelativeGraphPath(projectRoot, join30(projectRoot, trimmed));
10234
+ if (projectRelative) candidates.add(projectRelative);
10235
+ for (const candidate of candidates) {
10236
+ const nodeId = `src:${candidate}`;
10237
+ if (snapshot.nodes.some((node) => node.id === nodeId)) return nodeId;
10238
+ }
10239
+ return snapshot.nodes.find((node) => {
10240
+ if (node.type !== "SourceArtifact") return false;
10241
+ const path = graphPayloadString(node.payload, "path");
10242
+ return Boolean(path && (path === trimmed || candidates.has(path)));
10243
+ })?.id ?? null;
10244
+ }
11134
10245
  function stripProjectArgs(args, startIndex = 1) {
11135
10246
  const stripped = [args[0]];
11136
10247
  for (let index = startIndex; index < args.length; index += 1) {
@@ -11193,7 +10304,7 @@ function printWorkspaceProjectSelection(workspaceInfo, commandName = "command")
11193
10304
  function printMonorepoSetupGuidance(workspaceInfo) {
11194
10305
  const candidate = firstWorkspaceCandidate(workspaceInfo);
11195
10306
  const attachedProjects = workspaceInfo.appCandidates.filter(
11196
- (appCandidate) => existsSync30(join32(workspaceInfo.workspaceRoot, appCandidate, "decantr.essence.json"))
10307
+ (appCandidate) => existsSync28(join30(workspaceInfo.workspaceRoot, appCandidate, "decantr.essence.json"))
11197
10308
  );
11198
10309
  const firstAttached = attachedProjects[0];
11199
10310
  console.log(heading2("Decantr Setup"));
@@ -11261,7 +10372,7 @@ function printMonorepoSetupGuidance(workspaceInfo) {
11261
10372
  function resolveWorkflowProject(flags, commandName = "command", options = {}) {
11262
10373
  const projectArg = flagString(flags, "project");
11263
10374
  const workspaceInfo = resolveWorkspaceInfo(process.cwd(), projectArg);
11264
- if (projectArg && options.requireExisting !== false && !existsSync30(workspaceInfo.appRoot)) {
10375
+ if (projectArg && options.requireExisting !== false && !existsSync28(workspaceInfo.appRoot)) {
11265
10376
  printProjectNotFound(projectArg, commandName);
11266
10377
  process.exitCode = 1;
11267
10378
  return null;
@@ -11269,7 +10380,7 @@ function resolveWorkflowProject(flags, commandName = "command", options = {}) {
11269
10380
  if (projectArg && options.requireAppCandidate && workspaceInfo.appCandidates.length > 0) {
11270
10381
  const normalizedProject = normalizedProjectPath(projectArg);
11271
10382
  const normalizedWorkspaceProject = normalizedProjectPath(
11272
- relative7(workspaceInfo.workspaceRoot, workspaceInfo.appRoot)
10383
+ relative6(workspaceInfo.workspaceRoot, workspaceInfo.appRoot)
11273
10384
  );
11274
10385
  const knownCandidate = normalizedProject ? workspaceInfo.appCandidates.includes(normalizedProject) || Boolean(
11275
10386
  normalizedWorkspaceProject && workspaceInfo.appCandidates.includes(normalizedWorkspaceProject)
@@ -11318,12 +10429,137 @@ function formatScanApplicability(status) {
11318
10429
  if (status === "not_applicable") return dim3("not applicable");
11319
10430
  return dim3("unknown");
11320
10431
  }
10432
+ function formatScanGraphPreviewStatus(status) {
10433
+ if (status === "current") return success3("current");
10434
+ if (status === "stale") return `${YELLOW12}stale or missing${RESET16}`;
10435
+ if (status === "needs_migration") return `${YELLOW12}needs migration${RESET16}`;
10436
+ if (status === "not_attached") return dim3("not attached");
10437
+ return dim3("unavailable");
10438
+ }
10439
+ function relativeGraphArtifactPath(projectRoot, artifactPath) {
10440
+ return relative6(projectRoot, artifactPath).replace(/\\/g, "/");
10441
+ }
10442
+ function buildScanGraphPreview(workspaceInfo, projectArg) {
10443
+ let artifacts;
10444
+ try {
10445
+ artifacts = buildGraphArtifacts(workspaceInfo.appRoot);
10446
+ } catch (error3) {
10447
+ const message = error3.message;
10448
+ const needsMigration = message.includes("Essence v4");
10449
+ return {
10450
+ status: needsMigration ? "needs_migration" : "unavailable",
10451
+ canPreview: false,
10452
+ readOnly: true,
10453
+ message: needsMigration ? "Existing Decantr contract needs Essence v4 before a typed Contract graph can be previewed." : message,
10454
+ nextCommand: needsMigration ? withProject("decantr migrate --to v4", projectArg) : null,
10455
+ staleArtifacts: [],
10456
+ snapshot: null,
10457
+ capsule: null,
10458
+ diff: null
10459
+ };
10460
+ }
10461
+ if (!artifacts) {
10462
+ return {
10463
+ status: "not_attached",
10464
+ canPreview: false,
10465
+ readOnly: true,
10466
+ message: "No decantr.essence.json found. Adopt the project to create the first typed Contract graph baseline.",
10467
+ nextCommand: withProject("decantr adopt --yes", projectArg),
10468
+ staleArtifacts: [],
10469
+ snapshot: null,
10470
+ capsule: null,
10471
+ diff: null
10472
+ };
10473
+ }
10474
+ const stale = artifacts.staleArtifacts.length > 0;
10475
+ const diffSummary = summarizeGraphDiff(artifacts.diff);
10476
+ return {
10477
+ status: stale ? "stale" : "current",
10478
+ canPreview: true,
10479
+ readOnly: true,
10480
+ message: stale ? "A typed Contract graph can be derived now, but saved graph artifacts are missing or stale." : "Typed Contract graph artifacts are current with the project-owned contract sources.",
10481
+ nextCommand: stale ? withProject("decantr graph", projectArg) : null,
10482
+ staleArtifacts: artifacts.staleArtifacts.map(
10483
+ (path) => displayProjectPath(workspaceInfo, relativeGraphArtifactPath(artifacts.projectRoot, path))
10484
+ ),
10485
+ snapshot: {
10486
+ id: artifacts.snapshot.id,
10487
+ schemaVersion: artifacts.snapshot.schema_version,
10488
+ sourceHash: artifacts.snapshot.source_hash,
10489
+ nodes: artifacts.snapshot.summary.nodes,
10490
+ edges: artifacts.snapshot.summary.edges,
10491
+ findings: artifacts.snapshot.summary.findings,
10492
+ evidence: artifacts.snapshot.summary.evidence,
10493
+ sourceArtifacts: artifacts.snapshot.nodes.filter((node) => node.type === "SourceArtifact").length
10494
+ },
10495
+ capsule: {
10496
+ cacheKey: artifacts.capsule.cache_key,
10497
+ routes: artifacts.capsule.summary.routes,
10498
+ components: artifacts.capsule.summary.components,
10499
+ tokens: artifacts.capsule.summary.tokens,
10500
+ localRules: artifacts.capsule.summary.local_rules,
10501
+ styleBridge: artifacts.capsule.summary.style_bridge,
10502
+ sourceArtifacts: artifacts.capsule.summary.source_artifacts,
10503
+ sourceArtifactLimit: artifacts.capsule.source_artifact_limit,
10504
+ sourceArtifactsTruncated: artifacts.capsule.source_artifacts_truncated,
10505
+ openFindings: artifacts.capsule.summary.open_findings
10506
+ },
10507
+ diff: {
10508
+ ops: diffSummary.total,
10509
+ findingsAdded: diffSummary.findings.added,
10510
+ findingsResolved: diffSummary.findings.resolved,
10511
+ evidenceAdded: diffSummary.evidence.added
10512
+ }
10513
+ };
10514
+ }
10515
+ function printScanGraphPreview(preview) {
10516
+ if (!preview) return;
10517
+ console.log(`${BOLD9}Typed Contract Graph${RESET16}`);
10518
+ console.log(` Status: ${formatScanGraphPreviewStatus(preview.status)}`);
10519
+ console.log(` Read-only: ${preview.readOnly ? "yes" : "no"}`);
10520
+ if (preview.snapshot && preview.capsule) {
10521
+ console.log(
10522
+ ` Snapshot: ${preview.snapshot.nodes} nodes, ${preview.snapshot.edges} edges`
10523
+ );
10524
+ console.log(
10525
+ ` Evidence: ${preview.snapshot.findings} finding nodes, ${preview.snapshot.evidence} evidence nodes, ${preview.snapshot.sourceArtifacts} sources`
10526
+ );
10527
+ console.log(
10528
+ ` Capsule: ${preview.capsule.routes} routes, ${preview.capsule.localRules} local rules, ${preview.capsule.styleBridge} style bridge mappings`
10529
+ );
10530
+ const sourceArtifactIndex = preview.capsule.sourceArtifactsTruncated ? `${preview.capsule.sourceArtifactLimit}/${preview.capsule.sourceArtifacts} source handles` : `${preview.capsule.sourceArtifacts} source handles`;
10531
+ console.log(` Source handles: ${sourceArtifactIndex}`);
10532
+ if (preview.diff) {
10533
+ const diffHints = [
10534
+ preview.diff.findingsAdded > 0 ? `${preview.diff.findingsAdded} finding added` : null,
10535
+ preview.diff.findingsResolved > 0 ? `${preview.diff.findingsResolved} finding resolved` : null,
10536
+ preview.diff.evidenceAdded > 0 ? `${preview.diff.evidenceAdded} evidence added` : null
10537
+ ].filter(Boolean);
10538
+ console.log(
10539
+ ` Diff: ${preview.diff.ops} ops${diffHints.length > 0 ? ` (${diffHints.join(", ")})` : ""}`
10540
+ );
10541
+ }
10542
+ }
10543
+ console.log(` ${dim3(preview.message)}`);
10544
+ if (preview.staleArtifacts.length > 0) {
10545
+ console.log(` Stale artifacts:${" "} ${preview.staleArtifacts.slice(0, 3).join(", ")}`);
10546
+ if (preview.staleArtifacts.length > 3) {
10547
+ console.log(` ${dim3(`...${preview.staleArtifacts.length - 3} more`)}`);
10548
+ }
10549
+ }
10550
+ if (preview.nextCommand) {
10551
+ console.log(` Next: ${cyan3(preview.nextCommand)}`);
10552
+ }
10553
+ console.log("");
10554
+ }
11321
10555
  function printScanReport(report) {
11322
10556
  console.log(heading2("Decantr Scan"));
11323
10557
  console.log(dim3("Read-only Brownfield reconnaissance. No files were written."));
11324
10558
  console.log("");
11325
10559
  console.log(`${BOLD9}Verdict${RESET16}`);
11326
- console.log(` ${formatScanApplicability(report.applicability.status)} ${report.applicability.label}`);
10560
+ console.log(
10561
+ ` ${formatScanApplicability(report.applicability.status)} ${report.applicability.label}`
10562
+ );
11327
10563
  console.log(
11328
10564
  ` Confidence: ${cyan3(`${report.confidence.score}/100`)} (${report.confidence.level})`
11329
10565
  );
@@ -11332,12 +10568,15 @@ function printScanReport(report) {
11332
10568
  }
11333
10569
  console.log("");
11334
10570
  console.log(`${BOLD9}Project${RESET16}`);
11335
- console.log(` Framework: ${cyan3(report.project.framework)}${report.project.frameworkVersion ? ` ${report.project.frameworkVersion}` : ""}`);
10571
+ console.log(
10572
+ ` Framework: ${cyan3(report.project.framework)}${report.project.frameworkVersion ? ` ${report.project.frameworkVersion}` : ""}`
10573
+ );
11336
10574
  console.log(` Package manager:${" "} ${report.project.packageManager}`);
11337
10575
  console.log(` Language: ${report.project.primaryLanguage}`);
11338
10576
  console.log(` TypeScript: ${report.project.hasTypeScript ? "yes" : "no"}`);
11339
10577
  console.log(` Decantr: ${report.project.hasDecantr ? "present" : "not attached"}`);
11340
10578
  console.log("");
10579
+ printScanGraphPreview(report.graphPreview);
11341
10580
  console.log(`${BOLD9}Routes And Styling${RESET16}`);
11342
10581
  console.log(` Routes: ${report.routes.count} (${report.routes.strategy})`);
11343
10582
  for (const route of report.routes.items.slice(0, 8)) {
@@ -11355,7 +10594,9 @@ function printScanReport(report) {
11355
10594
  console.log("");
11356
10595
  if (report.staticHosting.githubPagesLikely || report.pagesProbe) {
11357
10596
  console.log(`${BOLD9}Published Surface${RESET16}`);
11358
- console.log(` GitHub Pages: ${report.staticHosting.githubPagesLikely ? "likely" : "not detected"}`);
10597
+ console.log(
10598
+ ` GitHub Pages: ${report.staticHosting.githubPagesLikely ? "likely" : "not detected"}`
10599
+ );
11359
10600
  if (report.source.publishedSiteUrl) {
11360
10601
  console.log(` Site URL: ${report.source.publishedSiteUrl}`);
11361
10602
  }
@@ -11395,11 +10636,13 @@ async function cmdScanWorkflow(args) {
11395
10636
  const report = await scanProjectReadOnly(workspaceInfo.appRoot, {
11396
10637
  input: { kind: "local", value: inputValue }
11397
10638
  });
10639
+ const graphPreview = buildScanGraphPreview(workspaceInfo, projectArg);
10640
+ const reportWithGraph = { ...report, graphPreview };
11398
10641
  if (jsonOutput) {
11399
- console.log(JSON.stringify(report, null, 2));
10642
+ console.log(JSON.stringify(reportWithGraph, null, 2));
11400
10643
  return;
11401
10644
  }
11402
- printScanReport(report);
10645
+ printScanReport(reportWithGraph);
11403
10646
  }
11404
10647
  async function cmdSetupWorkflow(args) {
11405
10648
  const { flags } = parseLooseArgs(args);
@@ -11421,9 +10664,9 @@ async function cmdSetupWorkflow(args) {
11421
10664
  console.log(` Detected: ${formatDetection(detected)}`);
11422
10665
  console.log("");
11423
10666
  if (detected.existingEssence) {
11424
- const hasLocalPatterns = existsSync30(localPatternsPath(workspaceInfo.appRoot));
11425
- const hasLocalRules = existsSync30(localRulesPath(workspaceInfo.appRoot));
11426
- const hasStyleBridge = existsSync30(styleBridgePath(workspaceInfo.appRoot));
10667
+ const hasLocalPatterns = existsSync28(localPatternsPath(workspaceInfo.appRoot));
10668
+ const hasLocalRules = existsSync28(localRulesPath(workspaceInfo.appRoot));
10669
+ const hasStyleBridge = existsSync28(styleBridgePath(workspaceInfo.appRoot));
11427
10670
  const verifyCommand = hasLocalPatterns || hasLocalRules ? "decantr verify --brownfield --local-patterns" : "decantr verify --brownfield";
11428
10671
  console.log(`${BOLD9}Recommended path:${RESET16} maintain an attached Decantr project`);
11429
10672
  console.log(
@@ -11441,7 +10684,9 @@ async function cmdSetupWorkflow(args) {
11441
10684
  console.log(
11442
10685
  ` ${cyan3(withProject(verifyCommand, projectArg))} Run local health and drift checks`
11443
10686
  );
11444
- console.log(` ${cyan3(withProject("decantr ci init", projectArg))} Wire the app into CI`);
10687
+ console.log(
10688
+ ` ${cyan3(withProject("decantr ci init", projectArg))} Wire the app into CI`
10689
+ );
11445
10690
  return;
11446
10691
  }
11447
10692
  if (hasFootprint) {
@@ -11509,7 +10754,7 @@ async function cmdAdoptWorkflow(args) {
11509
10754
  const hydratePacks = flagBoolean(flags, "packs", true) && !flagBoolean(flags, "skip-packs") && !flagBoolean(flags, "offline") && process.env.DECANTR_OFFLINE !== "true";
11510
10755
  const initCi = flagBoolean(flags, "ci") || flagBoolean(flags, "init-ci");
11511
10756
  const assistantBridge = flagString(flags, "assistant-bridge");
11512
- const hasEssence = existsSync30(join32(projectRoot, "decantr.essence.json"));
10757
+ const hasEssence = existsSync28(join30(projectRoot, "decantr.essence.json"));
11513
10758
  const proposalFlag = flagBoolean(flags, "replace-essence") ? "--replace-essence" : flagBoolean(flags, "merge-proposal") || hasEssence ? "--merge-proposal" : "--accept-proposal";
11514
10759
  const steps = [
11515
10760
  "analyze current app and write .decantr/brownfield intelligence",
@@ -11518,6 +10763,7 @@ async function cmdAdoptWorkflow(args) {
11518
10763
  if (hydratePacks) {
11519
10764
  steps.push("hydrate hosted execution packs into the app context");
11520
10765
  }
10766
+ steps.push("write typed Contract graph baseline");
11521
10767
  if (runVerify) {
11522
10768
  steps.push(
11523
10769
  runBrowser ? "verify with Project Health, browser evidence, visual manifest, and baseline" : "verify with Project Health and baseline"
@@ -11557,7 +10803,7 @@ async function cmdAdoptWorkflow(args) {
11557
10803
  if (hydratePacks) {
11558
10804
  try {
11559
10805
  const { bundle, contextDir } = await compileHostedExecutionPackBundle(
11560
- join32(projectRoot, "decantr.essence.json")
10806
+ join30(projectRoot, "decantr.essence.json")
11561
10807
  );
11562
10808
  const written = writeHostedExecutionPackContextArtifacts(
11563
10809
  contextDir,
@@ -11579,8 +10825,10 @@ async function cmdAdoptWorkflow(args) {
11579
10825
  } else if (flagBoolean(flags, "offline") || process.env.DECANTR_OFFLINE === "true") {
11580
10826
  console.log(dim3("Skipping hosted pack hydration in offline mode."));
11581
10827
  }
10828
+ await cmdGraph(projectRoot, { displayRoot: process.cwd() });
10829
+ if (process.exitCode && process.exitCode !== 0) return;
11582
10830
  if (runVerify) {
11583
- const { cmdHealth } = await import("./health-B4W7UJBZ.js");
10831
+ const { cmdHealth } = await import("./health-DDT4RABT.js");
11584
10832
  await cmdHealth(projectRoot, {
11585
10833
  browser: runBrowser,
11586
10834
  browserBaseUrl: baseUrl,
@@ -11651,7 +10899,7 @@ async function cmdVerifyWorkflow(args) {
11651
10899
  return;
11652
10900
  }
11653
10901
  if (workspaceMode) {
11654
- const { cmdWorkspace } = await import("./workspace-7RU77ZZW.js");
10902
+ const { cmdWorkspace } = await import("./workspace-3XNCMZ5L.js");
11655
10903
  await cmdWorkspace(process.cwd(), ["workspace", "health", ...withoutWorkflowOnlyFlags(args)]);
11656
10904
  return;
11657
10905
  }
@@ -11684,7 +10932,7 @@ async function cmdVerifyWorkflow(args) {
11684
10932
  }
11685
10933
  let guardExitCode;
11686
10934
  if (brownfield) {
11687
- const { cmdHeal, collectCheckIssues } = await import("./heal-IRIDB7IZ.js");
10935
+ const { cmdHeal, collectCheckIssues } = await import("./heal-HHVVTVIH.js");
11688
10936
  if (quietOutput) {
11689
10937
  const result = collectCheckIssues(workspaceInfo.appRoot, { brownfield: true });
11690
10938
  guardExitCode = result.issues.some((issue) => issue.type === "error") ? 1 : void 0;
@@ -11694,7 +10942,7 @@ async function cmdVerifyWorkflow(args) {
11694
10942
  process.exitCode = void 0;
11695
10943
  }
11696
10944
  }
11697
- const { cmdHealth, parseHealthArgs } = await import("./health-B4W7UJBZ.js");
10945
+ const { cmdHealth, parseHealthArgs } = await import("./health-DDT4RABT.js");
11698
10946
  await cmdHealth(workspaceInfo.appRoot, parseHealthArgs(healthArgs));
11699
10947
  if (localPatterns) {
11700
10948
  const validation = validateLocalLaw(workspaceInfo.appRoot);
@@ -11763,9 +11011,9 @@ async function cmdVerifyWorkflow(args) {
11763
11011
  }
11764
11012
  }
11765
11013
  function readJsonIfPresent(path) {
11766
- if (!existsSync30(path)) return null;
11014
+ if (!existsSync28(path)) return null;
11767
11015
  try {
11768
- return JSON.parse(readFileSync23(path, "utf-8"));
11016
+ return JSON.parse(readFileSync21(path, "utf-8"));
11769
11017
  } catch {
11770
11018
  return null;
11771
11019
  }
@@ -11846,6 +11094,60 @@ function createTaskAuthoritySummary(input) {
11846
11094
  }
11847
11095
  return { lane, sourceAuthority, styleAuthority, activeAuthorities, runtimeBoundary, warnings };
11848
11096
  }
11097
+ function behaviorTaskKeywords(task) {
11098
+ return [
11099
+ ...new Set(
11100
+ task.toLowerCase().split(/[^a-z0-9_-]+/).map((term) => term.trim()).filter((term) => term.length >= 3)
11101
+ )
11102
+ ];
11103
+ }
11104
+ function rankBehaviorObligationsForTask(obligations, routePatterns, taskSummary) {
11105
+ const routePatternSet = new Set(routePatterns.map((pattern) => pattern.toLowerCase()));
11106
+ const keywords = behaviorTaskKeywords(taskSummary);
11107
+ const ranked = obligations.map((entry) => {
11108
+ const haystack = [
11109
+ entry.patternId,
11110
+ entry.patternRole,
11111
+ entry.intent,
11112
+ ...entry.riskProfile,
11113
+ ...entry.componentPaths,
11114
+ ...entry.obligations.flatMap((obligation) => [obligation.id, obligation.label])
11115
+ ].filter((value) => typeof value === "string").join(" ").toLowerCase();
11116
+ const reasons = [];
11117
+ let score = 0;
11118
+ if (routePatternSet.has(entry.patternId.toLowerCase())) {
11119
+ score += 5;
11120
+ reasons.push("route_pattern");
11121
+ }
11122
+ for (const keyword of keywords) {
11123
+ if (!haystack.includes(keyword)) continue;
11124
+ score += 2;
11125
+ reasons.push(`task:${keyword}`);
11126
+ }
11127
+ if (/dialog|modal|confirm|delete|destructive|remove|account/.test(taskSummary)) {
11128
+ if (/dialog|modal|confirm|destructive/.test(haystack)) {
11129
+ score += 3;
11130
+ reasons.push("interaction_intent");
11131
+ }
11132
+ }
11133
+ if (/form|input|field|label|submit|validation/.test(taskSummary)) {
11134
+ if (/form|input|label|submit|validation/.test(haystack)) {
11135
+ score += 3;
11136
+ reasons.push("form_intent");
11137
+ }
11138
+ }
11139
+ return {
11140
+ ...entry,
11141
+ relevance: {
11142
+ score,
11143
+ reasons: reasons.length > 0 ? [...new Set(reasons)] : ["accepted_local_law"]
11144
+ }
11145
+ };
11146
+ });
11147
+ return ranked.sort(
11148
+ (a, b) => b.relevance.score - a.relevance.score || a.patternId.localeCompare(b.patternId)
11149
+ );
11150
+ }
11849
11151
  async function cmdTaskWorkflow(args) {
11850
11152
  const { flags, positional } = parseLooseArgs(args);
11851
11153
  const workspaceInfo = resolveWorkflowProject(flags, "task");
@@ -11863,7 +11165,7 @@ async function cmdTaskWorkflow(args) {
11863
11165
  }
11864
11166
  const route = routeInput.startsWith("/") ? routeInput : `/${routeInput}`;
11865
11167
  const taskSummary = positional.slice(1).join(" ").trim();
11866
- const essencePath = join32(workspaceInfo.appRoot, "decantr.essence.json");
11168
+ const essencePath = join30(workspaceInfo.appRoot, "decantr.essence.json");
11867
11169
  const essence = readJsonIfPresent(essencePath);
11868
11170
  if (!essence) {
11869
11171
  console.error(
@@ -11872,7 +11174,7 @@ async function cmdTaskWorkflow(args) {
11872
11174
  process.exitCode = 1;
11873
11175
  return;
11874
11176
  }
11875
- if (!isV49(essence)) {
11177
+ if (!isV48(essence)) {
11876
11178
  console.error(error2("Task context requires Essence v4. Run `decantr migrate --to v4` first."));
11877
11179
  process.exitCode = 1;
11878
11180
  return;
@@ -11887,17 +11189,29 @@ async function cmdTaskWorkflow(args) {
11887
11189
  }
11888
11190
  const section = essence.blueprint.sections.find((entry) => entry.id === target.section);
11889
11191
  const page = section?.pages.find((entry) => entry.id === target.page);
11890
- const contextDir = join32(workspaceInfo.appRoot, ".decantr", "context");
11891
- const manifest = readJsonIfPresent(join32(contextDir, "pack-manifest.json"));
11892
- const projectJson = readJsonIfPresent(join32(workspaceInfo.appRoot, ".decantr", "project.json"));
11192
+ const contextDir = join30(workspaceInfo.appRoot, ".decantr", "context");
11193
+ const manifest = readJsonIfPresent(join30(contextDir, "pack-manifest.json"));
11194
+ const projectJson = readJsonIfPresent(join30(workspaceInfo.appRoot, ".decantr", "project.json"));
11893
11195
  const pagePack = manifest?.pages?.find((entry) => entry.id === target.page);
11894
11196
  const sectionPack = manifest?.sections?.find((entry) => entry.id === target.section);
11895
- const visualManifest = readJsonIfPresent(join32(workspaceInfo.appRoot, ".decantr", "evidence", "visual-manifest.json"));
11197
+ const visualManifest = readJsonIfPresent(join30(workspaceInfo.appRoot, ".decantr", "evidence", "visual-manifest.json"));
11896
11198
  const screenshot = visualManifest?.routes?.find((entry) => entry.route === route)?.screenshot;
11897
11199
  const localPatternPackPath = localPatternsPath(workspaceInfo.appRoot);
11898
11200
  const localRuleManifestPath = localRulesPath(workspaceInfo.appRoot);
11899
11201
  const acceptedStyleBridgePath = styleBridgePath(workspaceInfo.appRoot);
11202
+ const graphDir = join30(workspaceInfo.appRoot, ".decantr", "graph");
11203
+ const contractCapsulePath = join30(graphDir, "contract-capsule.json");
11204
+ const graphSnapshotPath = join30(graphDir, "graph.snapshot.json");
11205
+ const contractCapsule = readJsonIfPresent(contractCapsulePath);
11206
+ const graphSnapshot = readJsonIfPresent(graphSnapshotPath);
11207
+ const routeGraphContext = buildGraphRouteContext(graphSnapshot, route, { task: taskSummary });
11208
+ const routePatterns = page?.layout?.map(extractPatternName) ?? [];
11900
11209
  const localLaw = createLocalLawTaskSummary(workspaceInfo.appRoot);
11210
+ const rankedBehaviorObligations = rankBehaviorObligationsForTask(
11211
+ localLaw.behaviorObligations,
11212
+ routePatterns,
11213
+ taskSummary
11214
+ );
11901
11215
  const styleBridge = createStyleBridgeTaskSummary(workspaceInfo.appRoot);
11902
11216
  const displayedStyleBridge = {
11903
11217
  ...styleBridge,
@@ -11906,20 +11220,35 @@ async function cmdTaskWorkflow(args) {
11906
11220
  const displayedLocalLaw = {
11907
11221
  ...localLaw,
11908
11222
  patternsPath: localLaw.patternsPath ? displayProjectPath(workspaceInfo, localLaw.patternsPath) : null,
11909
- rulesPath: localLaw.rulesPath ? displayProjectPath(workspaceInfo, localLaw.rulesPath) : null
11223
+ rulesPath: localLaw.rulesPath ? displayProjectPath(workspaceInfo, localLaw.rulesPath) : null,
11224
+ behaviorObligations: rankedBehaviorObligations
11910
11225
  };
11911
11226
  const changedSince = flagString(flags, "since");
11912
11227
  const currentChangedFiles = changedFiles(workspaceInfo.appRoot, changedSince);
11913
11228
  const changedRoutes = routeImpacts(workspaceInfo.appRoot, currentChangedFiles);
11229
+ const changedFileSourceNodes = graphSnapshot ? currentChangedFiles.map((file) => ({
11230
+ file,
11231
+ nodeId: graphSourceNodeIdForTaskFile(workspaceInfo.appRoot, graphSnapshot, file)
11232
+ })) : [];
11233
+ const changedFileSourceNodeIds = [
11234
+ ...new Set(
11235
+ changedFileSourceNodes.map((entry) => entry.nodeId).filter((nodeId) => Boolean(nodeId))
11236
+ )
11237
+ ];
11238
+ const changedFileMissingFiles = graphSnapshot ? changedFileSourceNodes.filter((entry) => !entry.nodeId).map((entry) => entry.file) : currentChangedFiles;
11239
+ const changedFileGraphContext = graphSnapshot && changedFileSourceNodeIds.length > 0 ? buildGraphImpactContext(graphSnapshot, changedFileSourceNodeIds, {
11240
+ task: taskSummary,
11241
+ limit: 120
11242
+ }) : null;
11914
11243
  const authority = createTaskAuthoritySummary({
11915
11244
  projectRoot: workspaceInfo.appRoot,
11916
11245
  workflowMode: projectJson?.initialized?.workflowMode ?? null,
11917
11246
  adoptionMode: projectJson?.initialized?.adoptionMode ?? null,
11918
- hasLocalPatterns: existsSync30(localPatternPackPath),
11919
- hasLocalRules: existsSync30(localRuleManifestPath),
11247
+ hasLocalPatterns: existsSync28(localPatternPackPath),
11248
+ hasLocalRules: existsSync28(localRuleManifestPath),
11920
11249
  hasPackManifest: Boolean(manifest),
11921
11250
  taskSummary,
11922
- hasStyleBridge: existsSync30(acceptedStyleBridgePath)
11251
+ hasStyleBridge: existsSync28(acceptedStyleBridgePath)
11923
11252
  });
11924
11253
  const context = {
11925
11254
  route,
@@ -11927,17 +11256,55 @@ async function cmdTaskWorkflow(args) {
11927
11256
  section: target.section,
11928
11257
  page: target.page,
11929
11258
  shell: page?.shell ?? section?.shell ?? null,
11930
- patterns: page?.layout?.map(extractPatternName) ?? [],
11259
+ patterns: routePatterns,
11931
11260
  read: [
11932
- pagePack ? displayProjectPath(workspaceInfo, join32(".decantr/context", pagePack.markdown)) : null,
11933
- sectionPack ? displayProjectPath(workspaceInfo, join32(".decantr/context", sectionPack.markdown)) : null,
11934
- manifest?.scaffold?.markdown ? displayProjectPath(workspaceInfo, join32(".decantr/context", manifest.scaffold.markdown)) : null,
11261
+ pagePack ? displayProjectPath(workspaceInfo, join30(".decantr/context", pagePack.markdown)) : null,
11262
+ sectionPack ? displayProjectPath(workspaceInfo, join30(".decantr/context", sectionPack.markdown)) : null,
11263
+ manifest?.scaffold?.markdown ? displayProjectPath(workspaceInfo, join30(".decantr/context", manifest.scaffold.markdown)) : null,
11935
11264
  displayProjectPath(workspaceInfo, ".decantr/context/scaffold.md"),
11936
11265
  displayProjectPath(workspaceInfo, "DECANTR.md"),
11937
- existsSync30(localPatternPackPath) ? displayProjectPath(workspaceInfo, ".decantr/local-patterns.json") : null,
11938
- existsSync30(localRuleManifestPath) ? displayProjectPath(workspaceInfo, ".decantr/rules.json") : null,
11939
- existsSync30(acceptedStyleBridgePath) ? displayProjectPath(workspaceInfo, ".decantr/style-bridge.json") : null
11266
+ existsSync28(localPatternPackPath) ? displayProjectPath(workspaceInfo, ".decantr/local-patterns.json") : null,
11267
+ existsSync28(localRuleManifestPath) ? displayProjectPath(workspaceInfo, ".decantr/rules.json") : null,
11268
+ existsSync28(acceptedStyleBridgePath) ? displayProjectPath(workspaceInfo, ".decantr/style-bridge.json") : null,
11269
+ contractCapsule ? displayProjectPath(workspaceInfo, ".decantr/graph/contract-capsule.json") : null,
11270
+ routeGraphContext && !contractCapsule ? displayProjectPath(workspaceInfo, ".decantr/graph/graph.snapshot.json") : null
11940
11271
  ].filter(Boolean),
11272
+ graph: contractCapsule || graphSnapshot ? {
11273
+ capsule: contractCapsule ? {
11274
+ path: displayProjectPath(workspaceInfo, ".decantr/graph/contract-capsule.json"),
11275
+ snapshotId: contractCapsule.snapshot_id ?? null,
11276
+ cacheKey: contractCapsule.cache_key ?? null,
11277
+ contractHash: contractCapsule.contract_hash ?? null,
11278
+ contractCacheKey: contractCapsule.contract_cache_key ?? null,
11279
+ summary: contractCapsule.summary ?? null
11280
+ } : null,
11281
+ snapshot: graphSnapshot ? {
11282
+ path: displayProjectPath(workspaceInfo, ".decantr/graph/graph.snapshot.json"),
11283
+ id: graphSnapshot.id ?? null,
11284
+ sourceHash: graphSnapshot.source_hash ?? null,
11285
+ summary: graphSnapshot.summary ?? null
11286
+ } : null,
11287
+ routeContext: routeGraphContext ? {
11288
+ path: displayProjectPath(workspaceInfo, ".decantr/graph/graph.snapshot.json"),
11289
+ ...routeGraphContext
11290
+ } : null,
11291
+ changedFileContext: currentChangedFiles.length > 0 ? {
11292
+ path: displayProjectPath(workspaceInfo, ".decantr/graph/graph.snapshot.json"),
11293
+ changedFiles: currentChangedFiles.slice(0, 40),
11294
+ resolvedNodeIds: changedFileSourceNodeIds,
11295
+ missingFiles: changedFileMissingFiles.slice(0, 40),
11296
+ impact: changedFileGraphContext ? {
11297
+ snapshotId: changedFileGraphContext.snapshotId,
11298
+ sourceHash: changedFileGraphContext.sourceHash,
11299
+ ranking: changedFileGraphContext.ranking,
11300
+ summary: changedFileGraphContext.summary,
11301
+ ids: changedFileGraphContext.ids,
11302
+ ranked: changedFileGraphContext.ranked.slice(0, 24),
11303
+ nodes: changedFileGraphContext.nodes,
11304
+ edges: changedFileGraphContext.edges
11305
+ } : null
11306
+ } : null
11307
+ } : null,
11941
11308
  screenshot: screenshot?.startsWith(".decantr/") ? displayProjectPath(workspaceInfo, screenshot) : screenshot ?? null,
11942
11309
  authority,
11943
11310
  localLaw: displayedLocalLaw,
@@ -11966,6 +11333,55 @@ async function cmdTaskWorkflow(args) {
11966
11333
  console.log(`${BOLD9}Visual evidence:${RESET16}`);
11967
11334
  console.log(` ${cyan3(context.screenshot)}`);
11968
11335
  }
11336
+ if (context.graph?.capsule || context.graph?.snapshot) {
11337
+ console.log("");
11338
+ console.log(`${BOLD9}Typed contract graph:${RESET16}`);
11339
+ if (context.graph.capsule) {
11340
+ console.log(` Capsule: ${cyan3(context.graph.capsule.path)}`);
11341
+ if (context.graph.capsule.cacheKey) {
11342
+ console.log(` Cache key: ${context.graph.capsule.cacheKey}`);
11343
+ }
11344
+ if (context.graph.capsule.contractCacheKey && context.graph.capsule.contractCacheKey !== context.graph.capsule.cacheKey) {
11345
+ console.log(` Contract cache key: ${context.graph.capsule.contractCacheKey}`);
11346
+ }
11347
+ }
11348
+ if (context.graph.snapshot) {
11349
+ const summary = context.graph.snapshot.summary;
11350
+ const counts = summary && typeof summary.nodes === "number" && typeof summary.edges === "number" ? ` (${summary.nodes} nodes, ${summary.edges} edges)` : "";
11351
+ console.log(` Snapshot: ${cyan3(context.graph.snapshot.path)}${counts}`);
11352
+ }
11353
+ if (context.graph.routeContext) {
11354
+ const routeSummary = context.graph.routeContext.summary;
11355
+ const routeFindings = context.graph.routeContext.nodes.filter((node) => node.type === "Finding").map((node) => graphPayloadString(node.payload, "code") ?? node.id).slice(0, 4);
11356
+ const routeSources = context.graph.routeContext.nodes.filter((node) => node.type === "SourceArtifact").map((node) => graphPayloadString(node.payload, "path") ?? node.id.replace(/^src:/, "")).slice(0, 4);
11357
+ const routeHints = [
11358
+ context.graph.routeContext.ids.patterns.length > 0 ? `patterns ${context.graph.routeContext.ids.patterns.join(", ")}` : null,
11359
+ routeSummary.openFindings > 0 ? `${routeSummary.openFindings} finding(s)` : null,
11360
+ routeSummary.evidence > 0 ? `${routeSummary.evidence} evidence node(s)` : null,
11361
+ routeSummary.sourceArtifacts > 0 ? `${routeSummary.sourceArtifacts} source artifact(s)` : null
11362
+ ].filter(Boolean);
11363
+ console.log(
11364
+ ` Route subgraph: ${routeSummary.nodes} nodes, ${routeSummary.edges} edges${routeHints.length > 0 ? `; ${routeHints.join("; ")}` : ""}`
11365
+ );
11366
+ if (routeFindings.length > 0) {
11367
+ console.log(` Route findings: ${routeFindings.join(", ")}`);
11368
+ }
11369
+ if (routeSources.length > 0) {
11370
+ console.log(` Route sources: ${routeSources.join(", ")}`);
11371
+ }
11372
+ }
11373
+ if (context.graph.changedFileContext?.impact) {
11374
+ const impact = context.graph.changedFileContext.impact;
11375
+ const impactHints = [
11376
+ impact.ids.routes.length > 0 ? `routes ${impact.ids.routes.join(", ")}` : null,
11377
+ impact.summary.pages > 0 ? `${impact.summary.pages} page(s)` : null,
11378
+ impact.summary.openFindings > 0 ? `${impact.summary.openFindings} finding(s)` : null
11379
+ ].filter(Boolean);
11380
+ console.log(
11381
+ ` Changed-file impact: ${impact.summary.nodes} nodes, ${impact.summary.edges} edges${impactHints.length > 0 ? `; ${impactHints.join("; ")}` : ""}`
11382
+ );
11383
+ }
11384
+ }
11969
11385
  console.log("");
11970
11386
  console.log(`${BOLD9}Authority for this task:${RESET16}`);
11971
11387
  console.log(` Lane: ${context.authority.lane}`);
@@ -11998,6 +11414,23 @@ async function cmdTaskWorkflow(args) {
11998
11414
  ].filter(Boolean).join(" | ");
11999
11415
  console.log(` ${pattern.id}: ${authorityHint}${pathHint}`);
12000
11416
  }
11417
+ if (context.localLaw.behaviorObligations.length > 0) {
11418
+ console.log("");
11419
+ console.log(`${BOLD9}Behavior obligations:${RESET16}`);
11420
+ for (const behavior of context.localLaw.behaviorObligations.slice(0, 3)) {
11421
+ const obligations = behavior.obligations.slice(0, 3).map((obligation) => obligation.label).join("; ");
11422
+ const reasons = behavior.relevance.reasons.slice(0, 3).join(", ");
11423
+ console.log(
11424
+ ` ${behavior.patternId}: ${behavior.intent ?? behavior.patternRole ?? "interaction law"} (${reasons})`
11425
+ );
11426
+ console.log(` ${obligations}`);
11427
+ }
11428
+ if (context.localLaw.behaviorObligations.length > 3) {
11429
+ console.log(
11430
+ dim3(` ...${context.localLaw.behaviorObligations.length - 3} more behavior pattern(s)`)
11431
+ );
11432
+ }
11433
+ }
12001
11434
  } else {
12002
11435
  console.log("");
12003
11436
  console.log(`${BOLD9}Project-owned local law:${RESET16}`);
@@ -12038,7 +11471,7 @@ async function cmdTaskWorkflow(args) {
12038
11471
  console.log("");
12039
11472
  console.log(`${BOLD9}LLM instruction:${RESET16}`);
12040
11473
  console.log(
12041
- " Preserve the active authority above. Use the route pack, section context, local laws, changed-file impact, and visual evidence as the task contract before changing code."
11474
+ " Preserve the active authority above. Use the route pack, section context, typed route graph, local laws, changed-file impact, and visual evidence as the task contract before changing code."
12042
11475
  );
12043
11476
  console.log(` After editing, run ${cyan3(context.verifyCommand)}.`);
12044
11477
  }
@@ -12064,7 +11497,7 @@ async function cmdCodifyWorkflow(args) {
12064
11497
  const workspaceInfo = resolveWorkflowProject(flags, "codify");
12065
11498
  if (!workspaceInfo) return;
12066
11499
  if (flagBoolean(flags, "accept")) {
12067
- if (!existsSync30(localPatternsProposalPath(workspaceInfo.appRoot)) && !existsSync30(localRulesProposalPath(workspaceInfo.appRoot)) && !existsSync30(styleBridgeProposalPath(workspaceInfo.appRoot))) {
11500
+ if (!existsSync28(localPatternsProposalPath(workspaceInfo.appRoot)) && !existsSync28(localRulesProposalPath(workspaceInfo.appRoot)) && !existsSync28(styleBridgeProposalPath(workspaceInfo.appRoot))) {
12068
11501
  console.error(
12069
11502
  error2(
12070
11503
  "No codify proposal found. Run `decantr codify --from-audit`, `decantr codify --style-bridge`, or `decantr codify` first."
@@ -12098,14 +11531,14 @@ async function cmdCodifyWorkflow(args) {
12098
11531
  }
12099
11532
  const detected = detectProject(workspaceInfo.appRoot);
12100
11533
  const essence = readJsonIfPresent(
12101
- join32(workspaceInfo.appRoot, "decantr.essence.json")
11534
+ join30(workspaceInfo.appRoot, "decantr.essence.json")
12102
11535
  );
12103
11536
  const fromAudit = flagBoolean(flags, "from-audit") || flagBoolean(flags, "discover-local-patterns") || flagBoolean(flags, "codify-local-patterns");
12104
11537
  const wantsStyleBridge = flagBoolean(flags, "style-bridge");
12105
11538
  const mapPatternSlug = flagString(flags, "map-pattern") ?? flagString(flags, "hosted-pattern");
12106
11539
  if (mapPatternSlug) {
12107
11540
  const registryClient = new RegistryClient({
12108
- cacheDir: join32(workspaceInfo.appRoot, ".decantr", "cache")
11541
+ cacheDir: join30(workspaceInfo.appRoot, ".decantr", "cache")
12109
11542
  });
12110
11543
  const candidates = await loadPatternDiscoveryCandidates(registryClient);
12111
11544
  const candidate = findPatternCandidateBySlug(candidates, mapPatternSlug);
@@ -12229,7 +11662,7 @@ async function cmdContentWorkflow(args) {
12229
11662
  }
12230
11663
  function cmdHelp() {
12231
11664
  console.log(`
12232
- ${BOLD9}decantr${RESET16} \u2014 Design intelligence for AI-generated UI
11665
+ ${BOLD9}decantr${RESET16} \u2014 AI Frontend Governance for codebases touched by AI agents
12233
11666
 
12234
11667
  ${BOLD9}Usage:${RESET16}
12235
11668
  decantr setup [--project <path>]
@@ -12238,6 +11671,7 @@ ${BOLD9}Usage:${RESET16}
12238
11671
  decantr adopt [--project <path>] [--base-url <url>] [--evidence] [--ci] [--no-packs] [--yes]
12239
11672
  decantr task <route> ["task summary"] [--project <path>] [--since origin/main] [--json]
12240
11673
  decantr verify [--project <path>] [--brownfield] [--local-patterns] [health options]
11674
+ decantr graph [--project <path>] [--route <route>] [--node <id>] [--file <path>] [--task <text>] [--snapshot-id <id>] [--compare-to <id>] [--capsule-source-limit <count>] [--check] [--json]
12241
11675
  decantr ci [--project <path>] [--workspace] [--fail-on error|warn|none]
12242
11676
  decantr doctor [--project <path>] [--workspace] [--json]
12243
11677
  decantr codify [--from-audit] [--style-bridge] [--map-pattern <slug>] [--accept] [--project <path>]
@@ -12254,6 +11688,7 @@ ${BOLD9}Advanced primitives:${RESET16}
12254
11688
  decantr check
12255
11689
  decantr check --brownfield
12256
11690
  decantr sync-drift
11691
+ decantr graph [--project <path>] [--route <route>] [--node <id>] [--file <path>] [--task <text>] [--snapshot-id <id>] [--compare-to <id>] [--capsule-source-limit <count>] [--check] [--json]
12257
11692
  decantr search <query> [--type <type>] [--sort <recommended|recent|name>] [--recommended] [--source <authored|benchmark|hybrid>]
12258
11693
  decantr suggest <query> [--type <type>] [--route <route>] [--file <path>] [--from-code]
12259
11694
  decantr get <type> <id>
@@ -12266,6 +11701,7 @@ ${BOLD9}Advanced primitives:${RESET16}
12266
11701
  decantr registry audit-project [--namespace <namespace>] [--json] [--essence <path>] [--dist <path>] [--sources <dir>]
12267
11702
  decantr health [--format text|json|markdown] [--ci] [--fail-on error|warn|none]
12268
11703
  decantr health --evidence [--browser] [--base-url <url>] [--design-tokens <path>]
11704
+ decantr health --diagnostics [--json|--markdown]
12269
11705
  decantr health --save-baseline | --since-baseline
12270
11706
  decantr health init-ci [legacy alias for decantr ci init]
12271
11707
  decantr ci init [--project <path>] [--workspace] [--provider github|generic] [--force]
@@ -12313,11 +11749,12 @@ ${BOLD9}Commands:${RESET16}
12313
11749
  ${cyan3("scan")} Read-only Brownfield reconnaissance; no files written
12314
11750
  ${cyan3("new")} Create a new greenfield workspace and bootstrap the available starter adapter
12315
11751
  ${cyan3("adopt")} Brownfield one-liner: analyze, attach, verify, and show next steps
12316
- ${cyan3("task")} Prepare route/task context, local law, evidence, and changed-file impact for an AI coding assistant
11752
+ ${cyan3("task")} Prepare route/task context, local law, behavior obligations, evidence, and changed-file impact for an AI coding assistant
12317
11753
  ${cyan3("verify")} One reliability gate over Project Health, Brownfield checks, baselines, and evidence
11754
+ ${cyan3("graph")} Build typed Contract graph artifacts and the agent cache capsule
12318
11755
  ${cyan3("ci")} Non-mutating CI gate and CI integration generator
12319
11756
  ${cyan3("doctor")} Explain Decantr state, artifact ownership, and the next command
12320
- ${cyan3("codify")} Propose or accept project-owned Brownfield UI patterns and rules
11757
+ ${cyan3("codify")} Propose or accept project-owned Brownfield UI patterns, behavior obligations, and rules
12321
11758
  ${cyan3("studio")} Open a local Project Health dashboard backed by the same report
12322
11759
  ${cyan3("content")} Content-author namespace: check, create, publish
12323
11760
 
@@ -12327,21 +11764,22 @@ ${BOLD9}Advanced commands:${RESET16}
12327
11764
  ${cyan3("status")} Show project status, DNA axioms, and blueprint info
12328
11765
  ${cyan3("health")} Advanced Project Health primitive [--json] [--markdown] [--ci]; use decantr ci for automation
12329
11766
  ${cyan3("workspace")} Discover and aggregate health across Decantr projects in a monorepo
12330
- ${cyan3("content-health")} Generate a local registry content health report [--json] [--markdown] [--ci]
12331
- ${cyan3("sync")} Sync registry content from API
11767
+ ${cyan3("content-health")} Generate a local official-vocabulary health report [--json] [--markdown] [--ci]
11768
+ ${cyan3("sync")} Sync official vocabulary from API
12332
11769
  ${cyan3("audit")} Audit the project or critique a specific file against compiled packs
12333
11770
  ${cyan3("migrate")} Migrate older essence files to v4 format (with .pre-v4.backup.json backup)
12334
11771
  ${cyan3("check")} Detect drift issues (validate + guard rules) [--telemetry] [--brownfield]
12335
11772
  ${cyan3("sync-drift")} Review and resolve drift log entries
12336
- ${cyan3("search")} Search the registry
11773
+ ${cyan3("graph")} Generate .decantr/graph snapshot, history, manifest, diff, and contract capsule
11774
+ ${cyan3("search")} Search official/community vocabulary
12337
11775
  ${cyan3("suggest")} Suggest patterns or alternatives for a query
12338
- ${cyan3("get")} Get full details of a registry item
11776
+ ${cyan3("get")} Get full details of a vocabulary item
12339
11777
  ${cyan3("list")} List items by type
12340
11778
  ${cyan3("showcase")} Inspect audited showcase benchmark metadata
12341
11779
  ${cyan3("validate")} Validate an Essence v4 file
12342
11780
  ${cyan3("theme")} Manage custom themes (create, list, validate, delete, import)
12343
11781
  ${cyan3("create")} Create a custom content item (pattern, theme, blueprint, etc.)
12344
- ${cyan3("publish")} Publish a custom content item to the community registry
11782
+ ${cyan3("publish")} Publish a custom vocabulary item to the community content service
12345
11783
  ${cyan3("login")} Authenticate with the Decantr registry
12346
11784
  ${cyan3("logout")} Remove stored credentials
12347
11785
  ${cyan3("analyze")} Brownfield entrypoint: scan an existing project and emit attach guidance
@@ -12361,6 +11799,11 @@ ${BOLD9}Examples:${RESET16}
12361
11799
  decantr adopt --project apps/web --yes
12362
11800
  decantr task /feed "add saved recipe actions"
12363
11801
  decantr verify --brownfield --local-patterns
11802
+ decantr graph --project apps/web
11803
+ decantr graph --project apps/web --route /feed --task "improve loading" --json
11804
+ decantr graph --project apps/web --file src/app/page.tsx --impact --json
11805
+ decantr graph --project apps/web --compare-to graph:previous --include-diff-ops --json
11806
+ decantr graph --check --json
12364
11807
  decantr verify --base-url http://localhost:3000 --evidence
12365
11808
  decantr verify --since-baseline
12366
11809
  decantr doctor --project apps/web
@@ -12418,6 +11861,7 @@ ${BOLD9}Workflow Model:${RESET16}
12418
11861
  ${cyan3("Brownfield preview")} decantr scan -> decantr adopt --yes
12419
11862
  ${cyan3("Brownfield monorepo")} decantr adopt --project apps/web --yes
12420
11863
  ${cyan3("Daily LLM work")} decantr task <route> "<change>" -> decantr verify --brownfield --local-patterns
11864
+ ${cyan3("Typed contract graph")} decantr graph -> agent session loads .decantr/graph/contract-capsule.json
12421
11865
  ${cyan3("Project-owned law")} decantr codify --from-audit -> edit proposal -> decantr codify --accept
12422
11866
  ${cyan3("Hybrid composition")} decantr add/remove, decantr theme switch, decantr registry, decantr upgrade
12423
11867
 
@@ -12480,6 +11924,7 @@ ${BOLD9}Usage:${RESET16}
12480
11924
  decantr health --ci [--fail-on error|warn|none]
12481
11925
  decantr health --prompt <finding-id>
12482
11926
  decantr health --evidence [--browser] [--design-tokens <path>]
11927
+ decantr health --diagnostics [--json|--markdown]
12483
11928
  decantr health --browser --base-url <url> --evidence
12484
11929
  decantr health --save-baseline
12485
11930
  decantr health --since-baseline
@@ -12494,6 +11939,7 @@ ${BOLD9}Options:${RESET16}
12494
11939
  --fail-on CI threshold: error, warn, or none
12495
11940
  --prompt Print an AI-ready remediation prompt for a finding
12496
11941
  --evidence Emit a local Evidence Bundle JSON artifact
11942
+ --diagnostics Print the stable diagnostic code and repair ID catalog
12497
11943
  --browser Include optional rendered-browser setup/evidence checks
12498
11944
  --base-url Base URL for rendered route checks when --browser is enabled
12499
11945
  --save-baseline Save the current health state for later comparison
@@ -12506,6 +11952,7 @@ ${BOLD9}Examples:${RESET16}
12506
11952
  decantr health --markdown --output decantr-health.md
12507
11953
  decantr ci --project apps/web
12508
11954
  decantr health --prompt audit-essence-missing
11955
+ decantr health --diagnostics --markdown
12509
11956
  decantr health --evidence --output .decantr/evidence/latest.json
12510
11957
  decantr ci init --project apps/web
12511
11958
  decantr ci init --workspace
@@ -12530,7 +11977,7 @@ ${BOLD9}Examples:${RESET16}
12530
11977
  }
12531
11978
  function cmdContentHealthHelp() {
12532
11979
  console.log(`
12533
- ${BOLD9}decantr content-health${RESET16} \u2014 Generate a local registry content health report
11980
+ ${BOLD9}decantr content-health${RESET16} \u2014 Generate a local official-vocabulary health report
12534
11981
 
12535
11982
  ${BOLD9}Usage:${RESET16}
12536
11983
  decantr content-health [--format text|json|markdown] [--output <file>]
@@ -12679,6 +12126,11 @@ ${BOLD9}decantr task${RESET16} \u2014 Prepare compact route/task context for an
12679
12126
  ${BOLD9}Usage:${RESET16}
12680
12127
  decantr task <route> ["task summary"] [--project <path>] [--since origin/main] [--json]
12681
12128
 
12129
+ ${BOLD9}Behavior:${RESET16}
12130
+ Includes accepted local law and behavior obligations, plus the typed contract capsule path when
12131
+ .decantr/graph exists. Run decantr graph first when you want graph-backed agent context in
12132
+ CLI-only workflows.
12133
+
12682
12134
  ${BOLD9}Examples:${RESET16}
12683
12135
  decantr task /feed "add saved recipe actions"
12684
12136
  decantr task /feed "add saved recipe actions" --since origin/main
@@ -12687,7 +12139,7 @@ ${BOLD9}Examples:${RESET16}
12687
12139
  }
12688
12140
  function cmdCodifyHelp() {
12689
12141
  console.log(`
12690
- ${BOLD9}decantr codify${RESET16} \u2014 Propose or accept project-owned Brownfield UI law and style bridges
12142
+ ${BOLD9}decantr codify${RESET16} \u2014 Propose or accept project-owned Brownfield UI law, behavior obligations, and style bridges
12691
12143
 
12692
12144
  ${BOLD9}Usage:${RESET16}
12693
12145
  decantr codify [--from-audit] [--style-bridge] [--project <path>]
@@ -12706,7 +12158,7 @@ ${BOLD9}Examples:${RESET16}
12706
12158
  }
12707
12159
  function cmdContentHelp() {
12708
12160
  console.log(`
12709
- ${BOLD9}decantr content${RESET16} \u2014 Content-author namespace for registry content repositories
12161
+ ${BOLD9}decantr content${RESET16} \u2014 Content-author namespace for official-vocabulary repositories
12710
12162
 
12711
12163
  ${BOLD9}Usage:${RESET16}
12712
12164
  decantr content check [content-health options]
@@ -12734,6 +12186,9 @@ function printCommandHelp(command, args) {
12734
12186
  case "verify":
12735
12187
  cmdVerifyHelp();
12736
12188
  return true;
12189
+ case "graph":
12190
+ cmdGraphHelp();
12191
+ return true;
12737
12192
  case "ci":
12738
12193
  cmdCiHelp();
12739
12194
  return true;
@@ -12785,10 +12240,10 @@ async function main() {
12785
12240
  if (command === "--version" || command === "-v" || command === "version") {
12786
12241
  try {
12787
12242
  const here = dirname6(fileURLToPath3(import.meta.url));
12788
- const candidates = [join32(here, "..", "package.json"), join32(here, "..", "..", "package.json")];
12243
+ const candidates = [join30(here, "..", "package.json"), join30(here, "..", "..", "package.json")];
12789
12244
  for (const candidate of candidates) {
12790
- if (existsSync30(candidate)) {
12791
- const pkg = JSON.parse(readFileSync23(candidate, "utf-8"));
12245
+ if (existsSync28(candidate)) {
12246
+ const pkg = JSON.parse(readFileSync21(candidate, "utf-8"));
12792
12247
  if (pkg.version) {
12793
12248
  console.log(pkg.version);
12794
12249
  return;
@@ -12827,6 +12282,51 @@ async function main() {
12827
12282
  await cmdVerifyWorkflow(args);
12828
12283
  break;
12829
12284
  }
12285
+ case "graph": {
12286
+ const { flags } = parseLooseArgs(args);
12287
+ if (!ensureAllowedFlags(
12288
+ flags,
12289
+ [
12290
+ "project",
12291
+ "route",
12292
+ "node",
12293
+ "file",
12294
+ "task",
12295
+ "snapshot-id",
12296
+ "compare-to",
12297
+ "include-diff-ops",
12298
+ "impact",
12299
+ "limit",
12300
+ "capsule-source-limit",
12301
+ "check",
12302
+ "json"
12303
+ ],
12304
+ "graph"
12305
+ ))
12306
+ break;
12307
+ const workspaceInfo = resolveWorkflowProject(flags, "graph");
12308
+ if (!workspaceInfo) break;
12309
+ const limitArg = flagString(flags, "limit");
12310
+ const limit = limitArg ? Number(limitArg) : void 0;
12311
+ const capsuleSourceLimitArg = flagString(flags, "capsule-source-limit");
12312
+ const capsuleSourceLimit = capsuleSourceLimitArg ? Number(capsuleSourceLimitArg) : void 0;
12313
+ await cmdGraph(workspaceInfo.appRoot, {
12314
+ check: flagBoolean(flags, "check"),
12315
+ json: flagBoolean(flags, "json"),
12316
+ route: flagString(flags, "route"),
12317
+ node: flagString(flags, "node"),
12318
+ file: flagString(flags, "file"),
12319
+ impact: flagBoolean(flags, "impact"),
12320
+ task: flagString(flags, "task"),
12321
+ snapshotId: flagString(flags, "snapshot-id"),
12322
+ compareTo: flagString(flags, "compare-to"),
12323
+ includeDiffOps: flagBoolean(flags, "include-diff-ops"),
12324
+ limit,
12325
+ capsuleSourceLimit,
12326
+ displayRoot: workspaceInfo.cwd
12327
+ });
12328
+ break;
12329
+ }
12830
12330
  case "ci": {
12831
12331
  await cmdCi(args, process.cwd());
12832
12332
  break;
@@ -12945,7 +12445,7 @@ async function main() {
12945
12445
  `${YELLOW12}Note: \`decantr heal\` is deprecated. Use \`decantr check\` instead.${RESET16}`
12946
12446
  );
12947
12447
  }
12948
- const { cmdHeal } = await import("./heal-IRIDB7IZ.js");
12448
+ const { cmdHeal } = await import("./heal-HHVVTVIH.js");
12949
12449
  const { flags } = parseLooseArgs(args);
12950
12450
  const workspaceInfo = resolveWorkflowProject(flags, "check");
12951
12451
  if (!workspaceInfo) break;
@@ -12970,7 +12470,7 @@ async function main() {
12970
12470
  const { flags } = parseLooseArgs(args);
12971
12471
  const workspaceInfo = resolveWorkflowProject(flags, "health");
12972
12472
  if (!workspaceInfo) break;
12973
- const { cmdHealth, parseHealthArgs } = await import("./health-B4W7UJBZ.js");
12473
+ const { cmdHealth, parseHealthArgs } = await import("./health-DDT4RABT.js");
12974
12474
  await cmdHealth(workspaceInfo.appRoot, parseHealthArgs(stripProjectArgs(args)));
12975
12475
  } catch (e) {
12976
12476
  console.error(error2(e.message));
@@ -12998,7 +12498,7 @@ async function main() {
12998
12498
  cmdStudioHelp();
12999
12499
  break;
13000
12500
  }
13001
- const { cmdStudio, parseStudioArgs } = await import("./studio-JOEECLI6.js");
12501
+ const { cmdStudio, parseStudioArgs } = await import("./studio-WDJ3BOCU.js");
13002
12502
  await cmdStudio(process.cwd(), parseStudioArgs(args));
13003
12503
  } catch (e) {
13004
12504
  console.error(error2(e.message));
@@ -13012,7 +12512,7 @@ async function main() {
13012
12512
  cmdWorkspaceHelp();
13013
12513
  break;
13014
12514
  }
13015
- const { cmdWorkspace } = await import("./workspace-7RU77ZZW.js");
12515
+ const { cmdWorkspace } = await import("./workspace-3XNCMZ5L.js");
13016
12516
  await cmdWorkspace(process.cwd(), args);
13017
12517
  } catch (e) {
13018
12518
  console.error(error2(e.message));
@@ -13109,7 +12609,7 @@ async function main() {
13109
12609
  if (!query && (route || file || fromCode)) {
13110
12610
  query = [
13111
12611
  route ? `route ${route}` : null,
13112
- file ? `file ${basename3(file)}` : null,
12612
+ file ? `file ${basename2(file)}` : null,
13113
12613
  fromCode ? "source code patterns" : null
13114
12614
  ].filter((entry) => Boolean(entry)).join(" ");
13115
12615
  }
@@ -13331,7 +12831,7 @@ async function main() {
13331
12831
  break;
13332
12832
  }
13333
12833
  if (packType === "page" && route && !id) {
13334
- const resolvedPath = essencePath ? resolveUserPath(essencePath) : join32(process.cwd(), "decantr.essence.json");
12834
+ const resolvedPath = essencePath ? resolveUserPath(essencePath) : join30(process.cwd(), "decantr.essence.json");
13335
12835
  id = resolvePagePackIdForRoute(resolvedPath, route);
13336
12836
  }
13337
12837
  await printHostedSelectedExecutionPack(