@decantr/cli 2.10.0 → 2.12.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,14 @@ import {
22
22
  listWorkspaceCandidates,
23
23
  listWorkspaceProjects,
24
24
  shouldFailWorkspaceHealth
25
- } from "./chunk-QDMXZSVK.js";
25
+ } from "./chunk-F6SWDGKO.js";
26
26
  import {
27
27
  createProjectHealthReport,
28
28
  formatProjectHealthMarkdown,
29
29
  formatProjectHealthText,
30
30
  resolveWorkspaceInfo,
31
31
  shouldFailHealth
32
- } from "./chunk-WPP3JEMC.js";
32
+ } from "./chunk-PHRVC575.js";
33
33
  import {
34
34
  buildGuardRegistryContext,
35
35
  createDoctrineMap,
@@ -46,11 +46,11 @@ import {
46
46
  sendCliCommandTelemetry,
47
47
  sendNewProjectCompletedTelemetry,
48
48
  writeDoctrineMap
49
- } from "./chunk-4SZ4SEKT.js";
49
+ } from "./chunk-CAKQWTZS.js";
50
50
 
51
51
  // src/index.ts
52
- import { existsSync as existsSync29, mkdirSync as mkdirSync16, readdirSync as readdirSync9, readFileSync as readFileSync22, writeFileSync as writeFileSync19 } from "fs";
53
- import { basename as basename3, dirname as dirname6, isAbsolute as isAbsolute3, join as join31, relative as relative7, resolve as resolve4 } from "path";
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";
54
54
  import { fileURLToPath as fileURLToPath3 } from "url";
55
55
  import { evaluateGuard, isV4 as isV49, validateEssence as validateEssence2 } from "@decantr/essence-spec";
56
56
  import {
@@ -88,6 +88,7 @@ Commit these when they exist:
88
88
  - \`.decantr/project.json\` - workflow, adoption mode, detection, and initialization metadata.
89
89
  - \`.decantr/local-patterns.json\` - project-owned Brownfield UI patterns accepted by the team.
90
90
  - \`.decantr/rules.json\` - project-owned mechanical rules accepted by the team.
91
+ - \`.decantr/style-bridge.json\` - accepted Hybrid style bridge from Decantr intent to project tokens/classes.
91
92
 
92
93
  ## Generated Context
93
94
 
@@ -2549,6 +2550,17 @@ function collectCssFiles(projectRoot) {
2549
2550
  function selectorFromMatch(raw) {
2550
2551
  return raw.replace(/\s+/g, " ").trim().slice(0, 120);
2551
2552
  }
2553
+ function isThemeVariantClass(id, selector) {
2554
+ if (/^(switcher|toggle|selector|picker|menu|button|btn|control|provider|container|panel|card|icon|label|group|actions?)$/i.test(
2555
+ id
2556
+ )) {
2557
+ return false;
2558
+ }
2559
+ const selectors = selector.split(",").map((entry) => entry.trim());
2560
+ return selectors.some(
2561
+ (entry) => /^(?::root|html|body)?(?:\.[\w-]+)*\.theme-[a-z0-9-]+(?:\s|$|:|\[|\.)/i.test(entry)
2562
+ );
2563
+ }
2552
2564
  function createThemeInventory(projectRoot, styling) {
2553
2565
  const files = collectCssFiles(projectRoot);
2554
2566
  const variantMap = /* @__PURE__ */ new Map();
@@ -2582,8 +2594,12 @@ function createThemeInventory(projectRoot, styling) {
2582
2594
  const themeClass = selector.match(/\.theme-([a-z0-9-]+)/i);
2583
2595
  if (/dark/i.test(selector)) id = "dark";
2584
2596
  if (dataTheme?.[1]) id = dataTheme[1].toLowerCase();
2585
- if (themeClass?.[1]) id = themeClass[1].toLowerCase();
2586
- if (selector.includes(":root")) id = "base";
2597
+ if (themeClass?.[1]) {
2598
+ const classId = themeClass[1].toLowerCase();
2599
+ if (!isThemeVariantClass(classId, selector)) continue;
2600
+ id = classId;
2601
+ }
2602
+ if (/^:root(?:\s|$|,)/.test(selector)) id = "base";
2587
2603
  ensureVariant(id, selector, rel2);
2588
2604
  }
2589
2605
  for (const [id, entry] of variantMap) {
@@ -2617,6 +2633,7 @@ function createThemeInventory(projectRoot, styling) {
2617
2633
  }
2618
2634
  const variants = [...variantMap.values()].sort((a, b) => a.id.localeCompare(b.id));
2619
2635
  const modes = variants.map((variant) => variant.id);
2636
+ const darkModeDetected = styling.darkMode || modes.includes("dark") || variants.some((variant) => /dark/i.test(variant.id));
2620
2637
  if (variants.length > 2) {
2621
2638
  notes.push(
2622
2639
  "Multiple theme variants were observed. Essence V4 remains unchanged; variants are reported here for task-time context."
@@ -2627,7 +2644,7 @@ function createThemeInventory(projectRoot, styling) {
2627
2644
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2628
2645
  localOnly: true,
2629
2646
  stylingApproach: styling.approach,
2630
- darkModeDetected: styling.darkMode,
2647
+ darkModeDetected,
2631
2648
  modes,
2632
2649
  variants,
2633
2650
  tokens: {
@@ -3217,8 +3234,8 @@ ${YELLOW2}Next step:${RESET2} Review ${BOLD}${reportDisplayPath}${RESET2}, then
3217
3234
  }
3218
3235
 
3219
3236
  // src/commands/ci.ts
3220
- import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
3221
- import { dirname as dirname3, join as join16, relative as relative4, resolve } from "path";
3237
+ import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "fs";
3238
+ import { dirname as dirname3, join as join17, relative as relative4, resolve } from "path";
3222
3239
 
3223
3240
  // src/local-law.ts
3224
3241
  import { execFileSync } from "child_process";
@@ -3309,7 +3326,8 @@ function createBrownfieldCodifyProposal(input) {
3309
3326
  ],
3310
3327
  componentPaths: evidence.buttonComponents,
3311
3328
  decide: "Define primary, secondary, tertiary, destructive, icon-only, disabled, and loading button variants from this app.",
3312
- evidence: evidence.buttonComponents.length ? evidence.buttonComponents : [
3329
+ classHints: evidence.buttonClassHints,
3330
+ evidence: evidence.buttonComponents.length ? evidence.buttonComponents : evidence.buttonClassHints.length ? evidence.buttonClassHints : [
3313
3331
  "No obvious Button wrapper found yet. Add the project-owned wrapper path before strict enforcement."
3314
3332
  ],
3315
3333
  forbiddenAlternatives: ["New one-off button variants without updating this manifest."]
@@ -3635,19 +3653,51 @@ function summarizeSourceEvidence(projectRoot, files) {
3635
3653
  formComponents: byName(["input", "field", "form", "select", "textarea"]),
3636
3654
  shellComponents,
3637
3655
  themeComponents,
3656
+ buttonClassHints: collectClassHints(projectRoot, files, [
3657
+ "button",
3658
+ "btn",
3659
+ "action",
3660
+ "primary",
3661
+ "secondary",
3662
+ "tertiary",
3663
+ "ghost",
3664
+ "link"
3665
+ ]),
3638
3666
  cardClassHints: collectClassHints(projectRoot, files, ["card", "panel", "surface", "tile"])
3639
3667
  };
3640
3668
  }
3641
3669
  function collectClassHints(projectRoot, files, terms) {
3642
3670
  const hints = /* @__PURE__ */ new Map();
3671
+ const wantsButton = terms.some(
3672
+ (term) => /^(button|btn|action|primary|secondary|tertiary|ghost|link)$/i.test(term)
3673
+ );
3674
+ const wantsSurface = terms.some((term) => /^(card|panel|surface|tile)$/i.test(term));
3675
+ const buttonSignal = /button|btn|action|primary|secondary|tertiary|ghost|link|destructive|icon/i;
3676
+ const surfaceSignal = /card|panel|surface|tile|rounded|shadow|border|bg-|p-\d|px-|py-/i;
3643
3677
  for (const file of files) {
3644
3678
  if (!UI_TEMPLATE_EXTENSIONS.has(extname(file.absolute))) continue;
3645
3679
  const content = readFileSync11(join15(projectRoot, file.relative), "utf-8");
3646
3680
  if (!terms.some((term) => content.toLowerCase().includes(term))) continue;
3681
+ if (wantsButton) {
3682
+ const openingTags = content.matchAll(
3683
+ /<([A-Za-z][\w.:/-]*)\b[^>]*\bclass(?:Name)?\s*=\s*["'`]([^"'`]+)["'`][^>]*>/g
3684
+ );
3685
+ for (const match of openingTags) {
3686
+ const tag = match[1];
3687
+ const value = match[2].trim();
3688
+ const tagLooksInteractive = /^(button|a|Link)$/i.test(tag) || /(^|\.)(Button|IconButton|LinkButton|Action)$/i.test(tag);
3689
+ const fileLooksInteractive = /button|action|link/i.test(basename2(file.relative));
3690
+ if (!tagLooksInteractive && !fileLooksInteractive) continue;
3691
+ if (!buttonSignal.test(value) && !fileLooksInteractive) continue;
3692
+ hints.set(value, (hints.get(value) ?? 0) + 1);
3693
+ }
3694
+ continue;
3695
+ }
3647
3696
  const matches = content.matchAll(/\bclass(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/g);
3648
3697
  for (const match of matches) {
3649
3698
  const value = match[1].trim();
3650
- if (!/(card|panel|surface|rounded|shadow|border|bg-|p-\d|px-|py-)/i.test(value)) continue;
3699
+ const keep = wantsSurface && surfaceSignal.test(value) || !wantsButton && !wantsSurface && (buttonSignal.test(value) || surfaceSignal.test(value));
3700
+ if (!keep) continue;
3651
3701
  hints.set(value, (hints.get(value) ?? 0) + 1);
3652
3702
  }
3653
3703
  }
@@ -3714,6 +3764,277 @@ function lineAt(contents, line) {
3714
3764
  return contents.split(/\r?\n/)[line - 1] ?? "";
3715
3765
  }
3716
3766
 
3767
+ // src/style-bridge.ts
3768
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
3769
+ import { join as join16 } from "path";
3770
+ function styleBridgeProposalPath(projectRoot) {
3771
+ return join16(projectRoot, ".decantr", "style-bridge.proposal.json");
3772
+ }
3773
+ function styleBridgePath(projectRoot) {
3774
+ return join16(projectRoot, ".decantr", "style-bridge.json");
3775
+ }
3776
+ function readJsonFile2(path) {
3777
+ if (!existsSync14(path)) return null;
3778
+ try {
3779
+ return JSON.parse(readFileSync12(path, "utf-8"));
3780
+ } catch {
3781
+ return null;
3782
+ }
3783
+ }
3784
+ function writeJsonFile(path, value) {
3785
+ writeFileSync10(path, `${JSON.stringify(value, null, 2)}
3786
+ `, "utf-8");
3787
+ }
3788
+ function readStyleBridge(projectRoot) {
3789
+ return readJsonFile2(styleBridgePath(projectRoot));
3790
+ }
3791
+ function readStyleBridgeProposal(projectRoot) {
3792
+ return readJsonFile2(styleBridgeProposalPath(projectRoot));
3793
+ }
3794
+ function stringArray(value) {
3795
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
3796
+ }
3797
+ function readThemeInventory(projectRoot) {
3798
+ const inventory = readJsonFile2(join16(projectRoot, ".decantr", "theme-inventory.json"));
3799
+ return {
3800
+ modes: stringArray(inventory?.modes),
3801
+ variantIds: Array.isArray(inventory?.variants) ? inventory.variants.map((variant) => variant.id).filter((id) => typeof id === "string") : [],
3802
+ darkModeDetected: typeof inventory?.darkModeDetected === "boolean" ? inventory.darkModeDetected : null
3803
+ };
3804
+ }
3805
+ function tokenHints(styling, terms) {
3806
+ return styling.cssVariables.filter((name) => terms.test(name)).slice(0, 12);
3807
+ }
3808
+ function readProjectPatternPack(projectRoot) {
3809
+ return readLocalPatternPack(projectRoot) ?? readJsonFile2(
3810
+ join16(projectRoot, ".decantr", "local-patterns.proposal.json")
3811
+ );
3812
+ }
3813
+ function classHintsForPattern(projectRoot, ids) {
3814
+ const pack = readProjectPatternPack(projectRoot);
3815
+ const classes = /* @__PURE__ */ new Set();
3816
+ for (const pattern of pack?.patterns ?? []) {
3817
+ if (!ids.includes(String(pattern.id))) continue;
3818
+ for (const hint of pattern.classHints ?? []) classes.add(hint);
3819
+ }
3820
+ return [...classes].slice(0, 12);
3821
+ }
3822
+ function sourceEvidence(projectRoot, ids) {
3823
+ const pack = readProjectPatternPack(projectRoot);
3824
+ const evidence = /* @__PURE__ */ new Set();
3825
+ for (const pattern of pack?.patterns ?? []) {
3826
+ if (!ids.includes(String(pattern.id))) continue;
3827
+ for (const path of pattern.componentPaths ?? []) evidence.add(path);
3828
+ for (const item of pattern.evidence ?? []) evidence.add(item);
3829
+ }
3830
+ return [...evidence].slice(0, 12);
3831
+ }
3832
+ function colorTokenNames(styling) {
3833
+ const names = new Set(Object.keys(styling.colors ?? {}));
3834
+ for (const variable of styling.cssVariables) {
3835
+ if (/color|bg|background|surface|text|foreground|border|primary|secondary|accent|muted/i.test(variable)) {
3836
+ names.add(variable);
3837
+ }
3838
+ }
3839
+ return [...names].slice(0, 40);
3840
+ }
3841
+ function createStyleBridgeProposal(input) {
3842
+ const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
3843
+ const theme = readThemeInventory(input.projectRoot);
3844
+ const routeCount = input.essence && typeof input.essence === "object" && "blueprint" in input.essence ? Object.keys(input.essence.blueprint?.routes ?? {}).length : 0;
3845
+ const target = input.essence && typeof input.essence === "object" && "meta" in input.essence ? String(input.essence.meta?.target ?? "") || null : null;
3846
+ const darkModeDetected = theme.darkModeDetected ?? input.styling.darkMode;
3847
+ const themeModes = theme.modes.length > 0 ? theme.modes : darkModeDetected ? ["base", "dark"] : ["base"];
3848
+ const themeVariantIds = theme.variantIds.length > 0 ? theme.variantIds : themeModes.filter((mode) => mode !== "base");
3849
+ return {
3850
+ version: 1,
3851
+ status: "proposal",
3852
+ generatedAt,
3853
+ source: "decantr codify --style-bridge",
3854
+ 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.",
3855
+ adoption: {
3856
+ mode: "style-bridge",
3857
+ workflowMode: "brownfield-attach",
3858
+ sourceAuthority: "Existing production source stays authoritative.",
3859
+ styleAuthority: "Use these mappings to translate Decantr concepts into project-owned tokens, classes, and components.",
3860
+ notRuntimeTakeover: true,
3861
+ authorityPrecedence: [
3862
+ "existing production source",
3863
+ "accepted style bridge",
3864
+ "accepted local patterns and rules",
3865
+ "Essence V4 contract",
3866
+ "hosted registry patterns and execution packs as optional guidance"
3867
+ ]
3868
+ },
3869
+ project: {
3870
+ framework: input.detected.framework,
3871
+ packageManager: input.detected.packageManager,
3872
+ target,
3873
+ routeCount
3874
+ },
3875
+ styling: {
3876
+ approach: input.styling.approach,
3877
+ configFile: input.styling.configFile ?? null,
3878
+ darkModeDetected,
3879
+ cssVariables: input.styling.cssVariables.slice(0, 80),
3880
+ colorTokenNames: colorTokenNames(input.styling),
3881
+ themeModes,
3882
+ themeVariantIds
3883
+ },
3884
+ mappings: [
3885
+ {
3886
+ id: "surface",
3887
+ label: "Surfaces and cards",
3888
+ decantrIntent: "surface background, card treatment, border, radius, depth, and hover state",
3889
+ projectAuthority: "Use the app card/surface primitives, tokens, and accepted local law.",
3890
+ tokenHints: tokenHints(input.styling, /surface|card|panel|bg|background|border|shadow|radius/i),
3891
+ classHints: classHintsForPattern(input.projectRoot, ["surface-card"]),
3892
+ sourceEvidence: sourceEvidence(input.projectRoot, ["surface-card"]),
3893
+ guardrails: [
3894
+ "Do not invent a new card color/radius/shadow recipe without updating local law.",
3895
+ "Do not add Decantr CSS d-* classes unless adoption mode changes to decantr-css."
3896
+ ]
3897
+ },
3898
+ {
3899
+ id: "action",
3900
+ label: "Actions and buttons",
3901
+ decantrIntent: "primary, secondary, tertiary, destructive, icon-only, loading, and disabled action states",
3902
+ projectAuthority: "Use the app button/action primitives and local variant names.",
3903
+ tokenHints: tokenHints(input.styling, /primary|secondary|accent|danger|error|destructive|focus/i),
3904
+ classHints: classHintsForPattern(input.projectRoot, ["button"]),
3905
+ sourceEvidence: sourceEvidence(input.projectRoot, ["button"]),
3906
+ guardrails: [
3907
+ "Map any new action style to primary/secondary/tertiary/destructive/icon before coding.",
3908
+ "Avoid raw button markup when a project-owned primitive exists."
3909
+ ]
3910
+ },
3911
+ {
3912
+ id: "focus-accessibility",
3913
+ label: "Focus and accessibility",
3914
+ decantrIntent: "visible focus, keyboard clarity, contrast, and reduced-motion safety",
3915
+ projectAuthority: "Use the app accessibility classes, focus tokens, and framework conventions.",
3916
+ tokenHints: tokenHints(input.styling, /focus|ring|outline|contrast|motion|duration/i),
3917
+ classHints: [],
3918
+ sourceEvidence: [],
3919
+ guardrails: [
3920
+ "Every new interactive control needs visible focus evidence.",
3921
+ "Motion should honor prefers-reduced-motion or an existing app motion helper."
3922
+ ]
3923
+ },
3924
+ {
3925
+ id: "layout-density",
3926
+ label: "Layout density and spacing",
3927
+ decantrIntent: "route gutters, section gaps, component padding, density, and responsive rhythm",
3928
+ projectAuthority: "Use existing layout wrappers, shell primitives, and spacing tokens/classes.",
3929
+ tokenHints: tokenHints(input.styling, /space|spacing|gap|gutter|container|radius/i),
3930
+ classHints: classHintsForPattern(input.projectRoot, ["page-shell"]),
3931
+ sourceEvidence: sourceEvidence(input.projectRoot, ["page-shell"]),
3932
+ guardrails: [
3933
+ "Do not let each page invent independent max-width, gutters, or scroll ownership.",
3934
+ "Use the detected shell/layout authority before adding a new wrapper."
3935
+ ]
3936
+ },
3937
+ {
3938
+ id: "theme-variant",
3939
+ label: "Theme variants",
3940
+ decantrIntent: "light, dark, brand, tenant, seasonal, and density variants",
3941
+ projectAuthority: "Use the app theme provider, data attributes, CSS variables, or Tailwind mode strategy.",
3942
+ tokenHints: tokenHints(input.styling, /theme|dark|light|brand|tenant|mode|color/i),
3943
+ classHints: classHintsForPattern(input.projectRoot, ["theme-variant"]),
3944
+ sourceEvidence: sourceEvidence(input.projectRoot, ["theme-variant"]),
3945
+ guardrails: [
3946
+ "Keep theme modes documented in .decantr/theme-inventory.json and this bridge.",
3947
+ "Do not treat theme switcher container classes as standalone theme variants."
3948
+ ]
3949
+ }
3950
+ ],
3951
+ rules: [
3952
+ "The bridge is advisory until accepted; after acceptance, task/doctor/CI should surface it as Hybrid authority.",
3953
+ "The bridge does not make hosted registry patterns enforceable. Map hosted concepts into local law first.",
3954
+ "Keep deterministic blocking checks in .decantr/rules.json, ESLint, Biome, tests, or visual regression."
3955
+ ],
3956
+ nextSteps: [
3957
+ "Review token and class hints. Replace generic hints with the exact project tokens/classes the team owns.",
3958
+ "Run decantr codify --accept to promote the proposal to .decantr/style-bridge.json.",
3959
+ "Use decantr task <route> before LLM edits so the style bridge appears in task context.",
3960
+ "Run decantr ci or decantr verify after edits to keep local law and Project Health visible."
3961
+ ]
3962
+ };
3963
+ }
3964
+ function writeStyleBridgeProposal(projectRoot, proposal) {
3965
+ const decantrDir = join16(projectRoot, ".decantr");
3966
+ mkdirSync9(decantrDir, { recursive: true });
3967
+ const path = styleBridgeProposalPath(projectRoot);
3968
+ writeJsonFile(path, proposal);
3969
+ return path;
3970
+ }
3971
+ function acceptStyleBridge(projectRoot) {
3972
+ const proposal = readStyleBridgeProposal(projectRoot);
3973
+ if (!proposal) return null;
3974
+ const accepted = {
3975
+ ...proposal,
3976
+ status: "accepted",
3977
+ acceptedAt: (/* @__PURE__ */ new Date()).toISOString()
3978
+ };
3979
+ const path = styleBridgePath(projectRoot);
3980
+ writeJsonFile(path, accepted);
3981
+ const projectJsonPath = join16(projectRoot, ".decantr", "project.json");
3982
+ const projectJson = readJsonFile2(projectJsonPath) ?? {};
3983
+ const initialized = typeof projectJson.initialized === "object" && projectJson.initialized !== null ? projectJson.initialized : {};
3984
+ writeJsonFile(projectJsonPath, {
3985
+ ...projectJson,
3986
+ initialized: {
3987
+ ...initialized,
3988
+ workflowMode: initialized.workflowMode ?? "brownfield-attach",
3989
+ adoptionMode: "style-bridge"
3990
+ }
3991
+ });
3992
+ return path;
3993
+ }
3994
+ function createStyleBridgeTaskSummary(projectRoot) {
3995
+ const bridge = readStyleBridge(projectRoot);
3996
+ return {
3997
+ path: bridge ? ".decantr/style-bridge.json" : null,
3998
+ status: bridge?.status ?? null,
3999
+ mappingCount: bridge?.mappings?.length ?? 0,
4000
+ stylingApproach: bridge?.styling?.approach ?? null,
4001
+ themeModes: bridge?.styling?.themeModes ?? [],
4002
+ mappings: bridge?.mappings?.map((mapping) => ({
4003
+ id: mapping.id,
4004
+ label: mapping.label,
4005
+ tokenHints: mapping.tokenHints.slice(0, 6),
4006
+ classHints: mapping.classHints.slice(0, 4),
4007
+ guardrails: mapping.guardrails.slice(0, 3)
4008
+ })) ?? []
4009
+ };
4010
+ }
4011
+ function styleBridgeMatches(projectRoot, query) {
4012
+ if (!projectRoot) return [];
4013
+ const bridge = readStyleBridge(projectRoot);
4014
+ if (!bridge) return [];
4015
+ 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]);
4016
+ if (terms.length === 0) return [];
4017
+ return bridge.mappings.map((mapping) => {
4018
+ const haystack = [
4019
+ mapping.id,
4020
+ mapping.label,
4021
+ mapping.decantrIntent,
4022
+ mapping.projectAuthority,
4023
+ ...mapping.tokenHints,
4024
+ ...mapping.classHints,
4025
+ ...mapping.guardrails
4026
+ ].join(" ").toLowerCase();
4027
+ const score = terms.reduce((sum, term) => sum + (haystack.includes(term) ? 1 : 0), 0);
4028
+ return {
4029
+ id: mapping.id,
4030
+ label: mapping.label,
4031
+ score,
4032
+ tokenHints: mapping.tokenHints.slice(0, 4),
4033
+ classHints: mapping.classHints.slice(0, 3)
4034
+ };
4035
+ }).filter((match) => match.score > 0).sort((a, b) => b.score - a.score || a.id.localeCompare(b.id)).slice(0, 5);
4036
+ }
4037
+
3717
4038
  // src/commands/ci.ts
3718
4039
  var BOLD2 = "\x1B[1m";
3719
4040
  var DIM3 = "\x1B[2m";
@@ -3722,9 +4043,9 @@ var RED2 = "\x1B[31m";
3722
4043
  var RESET3 = "\x1B[0m";
3723
4044
  var CI_SCHEMA = "https://decantr.ai/schemas/decantr-ci-report.v1.json";
3724
4045
  function readJson(path) {
3725
- if (!existsSync14(path)) return null;
4046
+ if (!existsSync15(path)) return null;
3726
4047
  try {
3727
- return JSON.parse(readFileSync12(path, "utf-8"));
4048
+ return JSON.parse(readFileSync13(path, "utf-8"));
3728
4049
  } catch {
3729
4050
  return null;
3730
4051
  }
@@ -3768,21 +4089,21 @@ function parseProvider(value) {
3768
4089
  throw new Error("Invalid --provider value. Use github or generic.");
3769
4090
  }
3770
4091
  function detectPackageManager2(root) {
3771
- const pkg = readJson(join16(root, "package.json"));
4092
+ const pkg = readJson(join17(root, "package.json"));
3772
4093
  const declared = pkg?.packageManager?.split("@")[0];
3773
4094
  if (declared === "pnpm" || declared === "npm" || declared === "yarn" || declared === "bun") {
3774
4095
  return declared;
3775
4096
  }
3776
- if (existsSync14(join16(root, "pnpm-lock.yaml"))) return "pnpm";
3777
- if (existsSync14(join16(root, "package-lock.json"))) return "npm";
3778
- if (existsSync14(join16(root, "yarn.lock"))) return "yarn";
3779
- if (existsSync14(join16(root, "bun.lock")) || existsSync14(join16(root, "bun.lockb"))) return "bun";
4097
+ if (existsSync15(join17(root, "pnpm-lock.yaml"))) return "pnpm";
4098
+ if (existsSync15(join17(root, "package-lock.json"))) return "npm";
4099
+ if (existsSync15(join17(root, "yarn.lock"))) return "yarn";
4100
+ if (existsSync15(join17(root, "bun.lock")) || existsSync15(join17(root, "bun.lockb"))) return "bun";
3780
4101
  return "unknown";
3781
4102
  }
3782
4103
  function hasWorkspaceMarker(root) {
3783
- const pkg = readJson(join16(root, "package.json"));
4104
+ const pkg = readJson(join17(root, "package.json"));
3784
4105
  return Boolean(
3785
- existsSync14(join16(root, "pnpm-workspace.yaml")) || existsSync14(join16(root, "turbo.json")) || existsSync14(join16(root, "nx.json")) || pkg?.workspaces
4106
+ existsSync15(join17(root, "pnpm-workspace.yaml")) || existsSync15(join17(root, "turbo.json")) || existsSync15(join17(root, "nx.json")) || pkg?.workspaces
3786
4107
  );
3787
4108
  }
3788
4109
  function installCommand(packageManager) {
@@ -3828,7 +4149,7 @@ function decantrCommand(packageManager) {
3828
4149
  }
3829
4150
  }
3830
4151
  function localCliPinned(root) {
3831
- const pkg = readJson(join16(root, "package.json"));
4152
+ const pkg = readJson(join17(root, "package.json"));
3832
4153
  return Boolean(pkg?.devDependencies?.["@decantr/cli"] || pkg?.dependencies?.["@decantr/cli"]);
3833
4154
  }
3834
4155
  function projectSlug(projectPath) {
@@ -3836,8 +4157,8 @@ function projectSlug(projectPath) {
3836
4157
  }
3837
4158
  function writeOutput(root, path, content) {
3838
4159
  const absolute = resolve(root, path);
3839
- mkdirSync9(dirname3(absolute), { recursive: true });
3840
- writeFileSync10(absolute, content, "utf-8");
4160
+ mkdirSync10(dirname3(absolute), { recursive: true });
4161
+ writeFileSync11(absolute, content, "utf-8");
3841
4162
  }
3842
4163
  function summarizeLocalLaw(projectRoot) {
3843
4164
  const validation = validateLocalLaw(projectRoot);
@@ -3845,15 +4166,154 @@ function summarizeLocalLaw(projectRoot) {
3845
4166
  checked: validation.patternPackPresent || validation.ruleManifestPresent,
3846
4167
  patternsPresent: validation.patternPackPresent,
3847
4168
  rulesPresent: validation.ruleManifestPresent,
4169
+ warnings: validation.warnings,
4170
+ findings: validation.findings.map((finding) => ({
4171
+ ruleId: finding.ruleId,
4172
+ severity: finding.severity,
4173
+ file: finding.file,
4174
+ line: finding.line,
4175
+ column: finding.column,
4176
+ message: finding.message,
4177
+ suggestedFix: finding.suggestedFix
4178
+ })),
3848
4179
  errorCount: validation.findings.filter((finding) => finding.severity === "error").length,
3849
4180
  warnCount: validation.findings.filter((finding) => finding.severity === "warn").length + validation.warnings.length
3850
4181
  };
3851
4182
  }
4183
+ function summarizeStyleBridge(projectRoot) {
4184
+ const summary = createStyleBridgeTaskSummary(projectRoot);
4185
+ const warnings = [];
4186
+ if (summary.path && summary.mappingCount === 0) {
4187
+ warnings.push(".decantr/style-bridge.json has no mappings.");
4188
+ }
4189
+ return {
4190
+ checked: Boolean(summary.path),
4191
+ present: Boolean(summary.path),
4192
+ status: summary.status,
4193
+ mappingCount: summary.mappingCount,
4194
+ stylingApproach: summary.stylingApproach,
4195
+ themeModes: summary.themeModes,
4196
+ warnings
4197
+ };
4198
+ }
3852
4199
  function localLawFails(summary, failOn) {
3853
4200
  if (failOn === "none" || !summary.checked) return false;
3854
4201
  if (summary.errorCount > 0) return true;
3855
4202
  return failOn === "warn" && summary.warnCount > 0;
3856
4203
  }
4204
+ function styleBridgeFails(summary, failOn) {
4205
+ if (failOn === "none" || !summary.checked) return false;
4206
+ return failOn === "warn" && summary.warnings.length > 0;
4207
+ }
4208
+ function projectCiStatus(health, localLaw, styleBridge) {
4209
+ if (health.status === "error" || localLaw.errorCount > 0) return "error";
4210
+ if (health.status === "warning" || localLaw.warnCount > 0 || styleBridge.warnings.length > 0) {
4211
+ return "warning";
4212
+ }
4213
+ return health.status;
4214
+ }
4215
+ function formatLocalLawText(summary, health) {
4216
+ const lines = ["", `${BOLD2}Project-owned local law:${RESET3}`];
4217
+ if (!summary.checked) {
4218
+ const isBrownfield = health.summary.workflowMode === "brownfield-attach";
4219
+ lines.push(
4220
+ isBrownfield ? " Not accepted yet. Run `decantr codify --from-audit`, review, then `decantr codify --accept`." : " Not active for this project."
4221
+ );
4222
+ return `${lines.join("\n")}
4223
+ `;
4224
+ }
4225
+ lines.push(
4226
+ ` Patterns: ${summary.patternsPresent ? "present" : "missing"} | Rules: ${summary.rulesPresent ? "present" : "missing"}`
4227
+ );
4228
+ lines.push(` Findings: ${summary.errorCount} error(s), ${summary.warnCount} warning(s)`);
4229
+ for (const warning of summary.warnings.slice(0, 5)) {
4230
+ lines.push(` ${DIM3}[WARN] ${warning}${RESET3}`);
4231
+ }
4232
+ for (const finding of summary.findings.slice(0, 8)) {
4233
+ const label = finding.severity.toUpperCase();
4234
+ lines.push(
4235
+ ` [${label}] ${finding.ruleId} ${finding.file}:${finding.line}:${finding.column} ${finding.message}`
4236
+ );
4237
+ lines.push(` ${DIM3}${finding.suggestedFix}${RESET3}`);
4238
+ }
4239
+ if (summary.findings.length > 8) {
4240
+ lines.push(` ${DIM3}...${summary.findings.length - 8} more local-law finding(s)${RESET3}`);
4241
+ }
4242
+ return `${lines.join("\n")}
4243
+ `;
4244
+ }
4245
+ function formatStyleBridgeText(summary) {
4246
+ const lines = ["", `${BOLD2}Project-owned style bridge:${RESET3}`];
4247
+ if (!summary.checked) {
4248
+ lines.push(" Not active for this project.");
4249
+ return `${lines.join("\n")}
4250
+ `;
4251
+ }
4252
+ lines.push(
4253
+ ` Present: ${summary.present ? "yes" : "no"} | Mappings: ${summary.mappingCount} | Styling: ${summary.stylingApproach ?? "unknown"}`
4254
+ );
4255
+ if (summary.themeModes.length > 0) {
4256
+ lines.push(` Theme modes: ${summary.themeModes.join(", ")}`);
4257
+ }
4258
+ for (const warning of summary.warnings.slice(0, 5)) {
4259
+ lines.push(` ${DIM3}[WARN] ${warning}${RESET3}`);
4260
+ }
4261
+ return `${lines.join("\n")}
4262
+ `;
4263
+ }
4264
+ function formatLocalLawMarkdown(summary, health) {
4265
+ const lines = ["## Project-Owned Local Law", ""];
4266
+ if (!summary.checked) {
4267
+ if (health.summary.workflowMode !== "brownfield-attach") {
4268
+ lines.push("Local law is not active for this project.");
4269
+ return lines.join("\n");
4270
+ }
4271
+ lines.push(
4272
+ "Local law has not been accepted yet. Run `decantr codify --from-audit`, review the proposal, then run `decantr codify --accept`."
4273
+ );
4274
+ return lines.join("\n");
4275
+ }
4276
+ lines.push(
4277
+ `Patterns: **${summary.patternsPresent ? "present" : "missing"}** \xB7 Rules: **${summary.rulesPresent ? "present" : "missing"}**`
4278
+ );
4279
+ lines.push("");
4280
+ lines.push(`Findings: **${summary.errorCount} error(s), ${summary.warnCount} warning(s)**`);
4281
+ if (summary.warnings.length > 0) {
4282
+ lines.push("");
4283
+ for (const warning of summary.warnings.slice(0, 5)) {
4284
+ lines.push(`- Warning: ${warning}`);
4285
+ }
4286
+ }
4287
+ if (summary.findings.length > 0) {
4288
+ lines.push("");
4289
+ for (const finding of summary.findings.slice(0, 12)) {
4290
+ lines.push(
4291
+ `- \`${finding.severity}\` \`${finding.ruleId}\` at \`${finding.file}:${finding.line}:${finding.column}\`: ${finding.message}`
4292
+ );
4293
+ }
4294
+ }
4295
+ return lines.join("\n");
4296
+ }
4297
+ function formatStyleBridgeMarkdown(summary) {
4298
+ const lines = ["## Project-Owned Style Bridge", ""];
4299
+ if (!summary.checked) {
4300
+ lines.push("Style bridge is not active for this project.");
4301
+ return lines.join("\n");
4302
+ }
4303
+ lines.push(
4304
+ `Present: **${summary.present ? "yes" : "no"}** \xB7 Mappings: **${summary.mappingCount}** \xB7 Styling: **${summary.stylingApproach ?? "unknown"}**`
4305
+ );
4306
+ if (summary.themeModes.length > 0) {
4307
+ lines.push("", `Theme modes: ${summary.themeModes.map((mode) => `\`${mode}\``).join(", ")}`);
4308
+ }
4309
+ if (summary.warnings.length > 0) {
4310
+ lines.push("");
4311
+ for (const warning of summary.warnings.slice(0, 5)) {
4312
+ lines.push(`- Warning: ${warning}`);
4313
+ }
4314
+ }
4315
+ return lines.join("\n");
4316
+ }
3857
4317
  function formatProjectCiMarkdown(report) {
3858
4318
  const lines = [
3859
4319
  "# Decantr CI",
@@ -3863,8 +4323,13 @@ function formatProjectCiMarkdown(report) {
3863
4323
  `- Status: **${report.status}**`,
3864
4324
  `- Fail on: \`${report.failOn}\``,
3865
4325
  `- Local law: ${report.localLaw.checked ? `${report.localLaw.errorCount} error(s), ${report.localLaw.warnCount} warning(s)` : "not accepted yet"}`,
4326
+ `- Style bridge: ${report.styleBridge.checked ? `${report.styleBridge.mappingCount} mapping(s)` : "not active"}`,
4327
+ "",
4328
+ formatProjectHealthMarkdown(report.health),
4329
+ "",
4330
+ formatLocalLawMarkdown(report.localLaw, report.health),
3866
4331
  "",
3867
- formatProjectHealthMarkdown(report.health)
4332
+ formatStyleBridgeMarkdown(report.styleBridge)
3868
4333
  ];
3869
4334
  return `${lines.join("\n")}
3870
4335
  `;
@@ -3955,7 +4420,7 @@ jobs:
3955
4420
  }
3956
4421
  function writeCiInit(root, options) {
3957
4422
  const workspaceInfo = resolveWorkspaceInfo(root, options.project);
3958
- if (options.project && !existsSync14(workspaceInfo.appRoot)) {
4423
+ if (options.project && !existsSync15(workspaceInfo.appRoot)) {
3959
4424
  throw new Error(`Project path does not exist: ${options.project}`);
3960
4425
  }
3961
4426
  if (workspaceInfo.requiresProjectSelection && !options.workspace) {
@@ -3978,7 +4443,7 @@ function writeCiInit(root, options) {
3978
4443
  if (provider === "generic") {
3979
4444
  const path2 = ".decantr/ci/decantr-ci.sh";
3980
4445
  const absolute2 = resolve(outputRoot, path2);
3981
- if (existsSync14(absolute2) && !options.force) {
4446
+ if (existsSync15(absolute2) && !options.force) {
3982
4447
  throw new Error(`${path2} already exists. Re-run with --force to replace it.`);
3983
4448
  }
3984
4449
  writeOutput(
@@ -3999,7 +4464,7 @@ function writeCiInit(root, options) {
3999
4464
  }
4000
4465
  const path = ".github/workflows/decantr-ci.yml";
4001
4466
  const absolute = resolve(outputRoot, path);
4002
- if (existsSync14(absolute) && !options.force) {
4467
+ if (existsSync15(absolute) && !options.force) {
4003
4468
  throw new Error(`${path} already exists. Re-run with --force to replace it.`);
4004
4469
  }
4005
4470
  writeOutput(
@@ -4048,7 +4513,7 @@ async function runWorkspaceCi(root, options) {
4048
4513
  }
4049
4514
  async function runProjectCi(root, options) {
4050
4515
  const workspaceInfo = resolveWorkspaceInfo(root, options.project);
4051
- if (options.project && !existsSync14(workspaceInfo.appRoot)) {
4516
+ if (options.project && !existsSync15(workspaceInfo.appRoot)) {
4052
4517
  console.error(`${RED2}Project path does not exist: ${options.project}${RESET3}`);
4053
4518
  return 1;
4054
4519
  }
@@ -4061,6 +4526,7 @@ async function runProjectCi(root, options) {
4061
4526
  const failOn = options.failOn ?? "error";
4062
4527
  const health = await createProjectHealthReport(workspaceInfo.appRoot);
4063
4528
  const localLaw = summarizeLocalLaw(workspaceInfo.appRoot);
4529
+ const styleBridge = summarizeStyleBridge(workspaceInfo.appRoot);
4064
4530
  const projectPath = workspaceInfo.appRoot === workspaceInfo.workspaceRoot ? null : workspaceInfo.appRoot.replace(`${workspaceInfo.workspaceRoot}/`, "");
4065
4531
  const report = {
4066
4532
  $schema: CI_SCHEMA,
@@ -4068,9 +4534,10 @@ async function runProjectCi(root, options) {
4068
4534
  mode: "project",
4069
4535
  projectPath,
4070
4536
  failOn,
4071
- status: health.status,
4537
+ status: projectCiStatus(health, localLaw, styleBridge),
4072
4538
  health,
4073
- localLaw
4539
+ localLaw,
4540
+ styleBridge
4074
4541
  };
4075
4542
  const json = `${JSON.stringify(report, null, 2)}
4076
4543
  `;
@@ -4080,9 +4547,12 @@ async function runProjectCi(root, options) {
4080
4547
  if (!options.output && !options.markdownOutput) {
4081
4548
  if (options.json) process.stdout.write(json);
4082
4549
  else if (options.markdown) process.stdout.write(markdown);
4083
- else process.stdout.write(formatProjectHealthText(health));
4550
+ else
4551
+ process.stdout.write(
4552
+ `${formatProjectHealthText(health)}${formatLocalLawText(localLaw, health)}${formatStyleBridgeText(styleBridge)}`
4553
+ );
4084
4554
  }
4085
- return shouldFailHealth(health, failOn) || localLawFails(localLaw, failOn) ? 1 : 0;
4555
+ return shouldFailHealth(health, failOn) || localLawFails(localLaw, failOn) || styleBridgeFails(styleBridge, failOn) ? 1 : 0;
4086
4556
  }
4087
4557
  function cmdCiHelp() {
4088
4558
  console.log(`
@@ -4117,8 +4587,8 @@ async function cmdCi(args = ["ci"], root = process.cwd()) {
4117
4587
  }
4118
4588
 
4119
4589
  // src/commands/create.ts
4120
- import { existsSync as existsSync15, mkdirSync as mkdirSync10, writeFileSync as writeFileSync11 } from "fs";
4121
- import { join as join17 } from "path";
4590
+ import { existsSync as existsSync16, mkdirSync as mkdirSync11, writeFileSync as writeFileSync12 } from "fs";
4591
+ import { join as join18 } from "path";
4122
4592
  import {
4123
4593
  CONTENT_TYPE_TO_API_CONTENT_TYPE,
4124
4594
  CONTENT_TYPES
@@ -4314,23 +4784,23 @@ function cmdCreate(type, name, projectRoot = process.cwd()) {
4314
4784
  }
4315
4785
  const contentType = type;
4316
4786
  const plural = PLURAL[contentType];
4317
- const customDir = join17(projectRoot, ".decantr", "custom", plural);
4318
- const filePath = join17(customDir, `${name}.json`);
4319
- if (existsSync15(filePath)) {
4787
+ const customDir = join18(projectRoot, ".decantr", "custom", plural);
4788
+ const filePath = join18(customDir, `${name}.json`);
4789
+ if (existsSync16(filePath)) {
4320
4790
  console.error(`${type} "${name}" already exists at ${filePath}`);
4321
4791
  process.exitCode = 1;
4322
4792
  return;
4323
4793
  }
4324
- mkdirSync10(customDir, { recursive: true });
4794
+ mkdirSync11(customDir, { recursive: true });
4325
4795
  const skeleton = getSkeleton(contentType, name, humanizeId(name));
4326
- writeFileSync11(filePath, JSON.stringify(skeleton, null, 2));
4796
+ writeFileSync12(filePath, JSON.stringify(skeleton, null, 2));
4327
4797
  console.log(`Created ${type} "${name}" at ${filePath}`);
4328
4798
  console.log(`Edit it, then publish with: decantr publish ${type} ${name}`);
4329
4799
  }
4330
4800
 
4331
4801
  // src/commands/doctor.ts
4332
- import { existsSync as existsSync16, readdirSync as readdirSync6, readFileSync as readFileSync13 } from "fs";
4333
- import { dirname as dirname4, join as join18, relative as relative5 } from "path";
4802
+ import { existsSync as existsSync17, readdirSync as readdirSync6, readFileSync as readFileSync14 } from "fs";
4803
+ import { dirname as dirname4, join as join19, relative as relative5 } from "path";
4334
4804
  import { fileURLToPath } from "url";
4335
4805
  import { isV4 as isV44 } from "@decantr/essence-spec";
4336
4806
  import { collectMissingPackManifestFiles } from "@decantr/verifier";
@@ -4342,25 +4812,25 @@ var YELLOW3 = "\x1B[33m";
4342
4812
  var CYAN2 = "\x1B[36m";
4343
4813
  var RESET4 = "\x1B[0m";
4344
4814
  function readJson2(path) {
4345
- if (!existsSync16(path)) return null;
4815
+ if (!existsSync17(path)) return null;
4346
4816
  try {
4347
- return JSON.parse(readFileSync13(path, "utf-8"));
4817
+ return JSON.parse(readFileSync14(path, "utf-8"));
4348
4818
  } catch {
4349
4819
  return null;
4350
4820
  }
4351
4821
  }
4352
4822
  function isContractOnlyProject(projectRoot) {
4353
- const projectJson = readJson2(join18(projectRoot, ".decantr", "project.json"));
4354
- return projectJson?.initialized?.adoptionMode === "contract-only";
4823
+ const projectJson = readJson2(join19(projectRoot, ".decantr", "project.json"));
4824
+ return projectJson?.initialized?.adoptionMode === "contract-only" || projectJson?.initialized?.adoptionMode === "style-bridge";
4355
4825
  }
4356
4826
  function readCliPackageVersion() {
4357
- const packagePath = join18(dirname4(fileURLToPath(import.meta.url)), "..", "..", "package.json");
4358
- const srcPackagePath = join18(dirname4(fileURLToPath(import.meta.url)), "..", "package.json");
4827
+ const packagePath = join19(dirname4(fileURLToPath(import.meta.url)), "..", "..", "package.json");
4828
+ const srcPackagePath = join19(dirname4(fileURLToPath(import.meta.url)), "..", "package.json");
4359
4829
  const pkg = readJson2(packagePath) ?? readJson2(srcPackagePath);
4360
4830
  return pkg?.version ?? "unknown";
4361
4831
  }
4362
4832
  function readPackageJson2(dir) {
4363
- return readJson2(join18(dir, "package.json"));
4833
+ return readJson2(join19(dir, "package.json"));
4364
4834
  }
4365
4835
  function detectPackageManager3(root) {
4366
4836
  const pkg = readPackageJson2(root);
@@ -4368,10 +4838,10 @@ function detectPackageManager3(root) {
4368
4838
  if (declared === "pnpm" || declared === "npm" || declared === "yarn" || declared === "bun") {
4369
4839
  return declared;
4370
4840
  }
4371
- if (existsSync16(join18(root, "pnpm-lock.yaml"))) return "pnpm";
4372
- if (existsSync16(join18(root, "package-lock.json"))) return "npm";
4373
- if (existsSync16(join18(root, "yarn.lock"))) return "yarn";
4374
- if (existsSync16(join18(root, "bun.lock")) || existsSync16(join18(root, "bun.lockb"))) return "bun";
4841
+ if (existsSync17(join19(root, "pnpm-lock.yaml"))) return "pnpm";
4842
+ if (existsSync17(join19(root, "package-lock.json"))) return "npm";
4843
+ if (existsSync17(join19(root, "yarn.lock"))) return "yarn";
4844
+ if (existsSync17(join19(root, "bun.lock")) || existsSync17(join19(root, "bun.lockb"))) return "bun";
4375
4845
  return "unknown";
4376
4846
  }
4377
4847
  function localCliDependency(root) {
@@ -4381,7 +4851,7 @@ function localCliDependency(root) {
4381
4851
  function hasWorkspaceMarker2(root) {
4382
4852
  const pkg = readPackageJson2(root);
4383
4853
  return Boolean(
4384
- existsSync16(join18(root, "pnpm-workspace.yaml")) || existsSync16(join18(root, "turbo.json")) || existsSync16(join18(root, "nx.json")) || pkg?.workspaces
4854
+ existsSync17(join19(root, "pnpm-workspace.yaml")) || existsSync17(join19(root, "turbo.json")) || existsSync17(join19(root, "nx.json")) || pkg?.workspaces
4385
4855
  );
4386
4856
  }
4387
4857
  function pinCliCommand2(packageManager, root) {
@@ -4404,8 +4874,8 @@ function rel(root, path) {
4404
4874
  }
4405
4875
  function hasAnyFile(dir, names) {
4406
4876
  for (const name of names) {
4407
- const path = join18(dir, name);
4408
- if (existsSync16(path)) return name;
4877
+ const path = join19(dir, name);
4878
+ if (existsSync17(path)) return name;
4409
4879
  }
4410
4880
  return null;
4411
4881
  }
@@ -4427,7 +4897,7 @@ function detectDesignAuthority(workspaceRoot, appRoot) {
4427
4897
  "libs/design-system"
4428
4898
  ];
4429
4899
  for (const candidate of sharedCandidates) {
4430
- if (existsSync16(join18(workspaceRoot, candidate))) found.add(candidate);
4900
+ if (existsSync17(join19(workspaceRoot, candidate))) found.add(candidate);
4431
4901
  }
4432
4902
  for (const root of [appRoot, workspaceRoot]) {
4433
4903
  const label = root === appRoot ? "app" : "workspace";
@@ -4438,9 +4908,9 @@ function detectDesignAuthority(workspaceRoot, appRoot) {
4438
4908
  "tailwind.config.cjs"
4439
4909
  ]);
4440
4910
  if (tailwind) found.add(`${label}:${tailwind}`);
4441
- if (existsSync16(join18(root, ".storybook"))) found.add(`${label}:.storybook`);
4442
- if (existsSync16(join18(root, "components", "ui"))) found.add(`${label}:components/ui`);
4443
- if (existsSync16(join18(root, "src", "components", "ui"))) found.add(`${label}:src/components/ui`);
4911
+ if (existsSync17(join19(root, ".storybook"))) found.add(`${label}:.storybook`);
4912
+ if (existsSync17(join19(root, "components", "ui"))) found.add(`${label}:components/ui`);
4913
+ if (existsSync17(join19(root, "src", "components", "ui"))) found.add(`${label}:src/components/ui`);
4444
4914
  if (packageHasDependency(root, ["@storybook/react", "storybook"]))
4445
4915
  found.add(`${label}:storybook`);
4446
4916
  if (packageHasDependency(root, ["tailwindcss"])) found.add(`${label}:tailwindcss`);
@@ -4452,18 +4922,18 @@ function detectDesignAuthority(workspaceRoot, appRoot) {
4452
4922
  }
4453
4923
  function findCiFiles(root) {
4454
4924
  const files = [];
4455
- const workflows = join18(root, ".github", "workflows");
4456
- if (existsSync16(workflows)) {
4925
+ const workflows = join19(root, ".github", "workflows");
4926
+ if (existsSync17(workflows)) {
4457
4927
  for (const entry of readdirSync6(workflows, { withFileTypes: true })) {
4458
4928
  if (!entry.isFile()) continue;
4459
- const path = join18(workflows, entry.name);
4460
- const text = readFileSync13(path, "utf-8");
4929
+ const path = join19(workflows, entry.name);
4930
+ const text = readFileSync14(path, "utf-8");
4461
4931
  if (text.includes("decantr")) files.push(rel(root, path));
4462
4932
  }
4463
4933
  }
4464
4934
  for (const candidate of ["Jenkinsfile", ".gitlab-ci.yml", "azure-pipelines.yml", "BUILD"]) {
4465
- const path = join18(root, candidate);
4466
- if (existsSync16(path) && readFileSync13(path, "utf-8").includes("decantr")) {
4935
+ const path = join19(root, candidate);
4936
+ if (existsSync17(path) && readFileSync14(path, "utf-8").includes("decantr")) {
4467
4937
  files.push(candidate);
4468
4938
  }
4469
4939
  }
@@ -4503,15 +4973,17 @@ function deriveAdoptionLane(input) {
4503
4973
  };
4504
4974
  }
4505
4975
  const hasLocalLaw = input.localPatternsPresent || input.localRulesPresent;
4976
+ const hasStyleBridge = input.styleBridgePresent || input.adoptionMode === "style-bridge";
4506
4977
  if (input.workflowMode === "hybrid-compose") {
4507
4978
  return {
4508
4979
  id: "hybrid-compose",
4509
4980
  label: "Hybrid composition",
4510
4981
  sourceAuthority: "Existing app plus selected Decantr/local law",
4511
- styleAuthority: input.adoptionMode === "decantr-css" ? "Decantr CSS runtime is active where adopted" : input.adoptionMode === "style-bridge" ? "Style bridge maps Decantr intent into the app styling system" : "Existing app styling system remains primary",
4982
+ styleAuthority: input.adoptionMode === "decantr-css" ? "Decantr CSS runtime is active where adopted" : hasStyleBridge ? "Style bridge maps Decantr intent into the app styling system" : "Existing app styling system remains primary",
4512
4983
  activeAuthorities: [
4513
4984
  "existing source",
4514
4985
  "Essence V4 contract",
4986
+ hasStyleBridge ? "accepted style bridge" : "no style bridge",
4515
4987
  hasLocalLaw ? "accepted local law" : "reviewed Hybrid choices",
4516
4988
  input.packManifestPresent ? "hosted execution packs" : "optional hosted packs"
4517
4989
  ],
@@ -4529,13 +5001,18 @@ function deriveAdoptionLane(input) {
4529
5001
  nextChoice: "Keep Decantr CSS usage explicit and validate route changes with task and verify."
4530
5002
  };
4531
5003
  }
4532
- if (input.adoptionMode === "style-bridge") {
5004
+ if (hasStyleBridge) {
4533
5005
  return {
4534
5006
  id: "hybrid-style-bridge",
4535
5007
  label: "Hybrid style bridge",
4536
5008
  sourceAuthority: "Existing app plus Decantr intent mapped through a style bridge",
4537
5009
  styleAuthority: "Style bridge over the existing app styling system",
4538
- activeAuthorities: ["existing source", "Essence V4 contract", "style bridge"],
5010
+ activeAuthorities: [
5011
+ "existing source",
5012
+ "Essence V4 contract",
5013
+ "accepted style bridge",
5014
+ hasLocalLaw ? "accepted local patterns/rules" : "optional local law"
5015
+ ],
4539
5016
  nextChoice: "Use local law to decide which component families the bridge governs before making it strict."
4540
5017
  };
4541
5018
  }
@@ -4593,18 +5070,18 @@ function buildDoctorReport(root, args) {
4593
5070
  const workspaceInfo = resolveWorkspaceInfo(root, projectArg);
4594
5071
  const workspaceRoot = workspaceInfo.workspaceRoot;
4595
5072
  const appRoot = workspaceMode ? workspaceRoot : workspaceInfo.appRoot;
4596
- const projectMissing = Boolean(projectArg && !existsSync16(appRoot));
5073
+ const projectMissing = Boolean(projectArg && !existsSync17(appRoot));
4597
5074
  const projectPath = appRoot === workspaceRoot ? null : rel(workspaceRoot, appRoot);
4598
5075
  const packageManager = detectPackageManager3(workspaceRoot);
4599
5076
  const cliDependency = localCliDependency(workspaceRoot);
4600
5077
  const detected = detectProject(appRoot);
4601
- const projectJson = readJson2(join18(appRoot, ".decantr", "project.json"));
4602
- const essence = readJson2(join18(appRoot, "decantr.essence.json"));
5078
+ const projectJson = readJson2(join19(appRoot, ".decantr", "project.json"));
5079
+ const essence = readJson2(join19(appRoot, "decantr.essence.json"));
4603
5080
  const essenceVersion = essence && typeof essence === "object" && "version" in essence ? String(essence.version) : null;
4604
- const contextDir = join18(appRoot, ".decantr", "context");
4605
- const packManifestPresent = existsSync16(join18(contextDir, "pack-manifest.json"));
4606
- const reviewPackPresent = existsSync16(join18(contextDir, "review-pack.json"));
4607
- const artifactReadmePresent = existsSync16(join18(appRoot, ARTIFACT_README_PATH));
5081
+ const contextDir = join19(appRoot, ".decantr", "context");
5082
+ const packManifestPresent = existsSync17(join19(contextDir, "pack-manifest.json"));
5083
+ const reviewPackPresent = existsSync17(join19(contextDir, "review-pack.json"));
5084
+ const artifactReadmePresent = existsSync17(join19(appRoot, ARTIFACT_README_PATH));
4608
5085
  const projects = listWorkspaceProjects(workspaceRoot);
4609
5086
  const candidates = listWorkspaceCandidates(workspaceRoot, projects).map((candidate) => ({
4610
5087
  path: candidate.path,
@@ -4614,21 +5091,23 @@ function buildDoctorReport(root, args) {
4614
5091
  const ciFiles = findCiFiles(workspaceRoot);
4615
5092
  const workflowMode = projectJson?.initialized?.workflowMode ?? null;
4616
5093
  const adoptionMode = projectJson?.initialized?.adoptionMode ?? null;
4617
- const packHydrationOptional = adoptionMode === "contract-only";
4618
- const localPatternsPresent = existsSync16(localPatternsPath(appRoot));
4619
- const localRulesPresent = existsSync16(localRulesPath(appRoot));
5094
+ const packHydrationOptional = adoptionMode === "contract-only" || adoptionMode === "style-bridge";
5095
+ const localPatternsPresent = existsSync17(localPatternsPath(appRoot));
5096
+ const localRulesPresent = existsSync17(localRulesPath(appRoot));
5097
+ const styleBridgePresent = existsSync17(styleBridgePath(appRoot));
5098
+ const styleBridgeProposalPresent = existsSync17(styleBridgeProposalPath(appRoot));
4620
5099
  const missingPackReferences = workspaceMode ? projects.flatMap(
4621
- (project) => collectMissingPackManifestFiles(join18(workspaceRoot, project.path)).map(
5100
+ (project) => collectMissingPackManifestFiles(join19(workspaceRoot, project.path)).map(
4622
5101
  (missing) => `${project.path}/${missing.relativePath}`
4623
5102
  )
4624
5103
  ) : collectMissingPackManifestFiles(appRoot).map((missing) => missing.relativePath);
4625
5104
  const workspaceProjectsMissingManifest = workspaceMode ? projects.map((project) => project.path).filter((projectPath2) => {
4626
- const projectContextDir = join18(workspaceRoot, projectPath2, ".decantr", "context");
4627
- return !isContractOnlyProject(join18(workspaceRoot, projectPath2)) && !existsSync16(join18(projectContextDir, "pack-manifest.json"));
5105
+ const projectContextDir = join19(workspaceRoot, projectPath2, ".decantr", "context");
5106
+ return !isContractOnlyProject(join19(workspaceRoot, projectPath2)) && !existsSync17(join19(projectContextDir, "pack-manifest.json"));
4628
5107
  }) : [];
4629
5108
  const workspaceProjectsMissingReviewPack = workspaceMode ? projects.map((project) => project.path).filter((projectPath2) => {
4630
- const projectContextDir = join18(workspaceRoot, projectPath2, ".decantr", "context");
4631
- return !isContractOnlyProject(join18(workspaceRoot, projectPath2)) && !existsSync16(join18(projectContextDir, "review-pack.json"));
5109
+ const projectContextDir = join19(workspaceRoot, projectPath2, ".decantr", "context");
5110
+ return !isContractOnlyProject(join19(workspaceRoot, projectPath2)) && !existsSync17(join19(projectContextDir, "review-pack.json"));
4632
5111
  }) : [];
4633
5112
  const issues = [];
4634
5113
  if (projectMissing) {
@@ -4685,7 +5164,7 @@ function buildDoctorReport(root, args) {
4685
5164
  nextCommand: "decantr refresh"
4686
5165
  });
4687
5166
  }
4688
- if (essenceVersion === "4.0.0" && !packHydrationOptional && (!existsSync16(contextDir) || !packManifestPresent)) {
5167
+ if (essenceVersion === "4.0.0" && !packHydrationOptional && (!existsSync17(contextDir) || !packManifestPresent)) {
4689
5168
  issues.push({
4690
5169
  category: "generated-artifact",
4691
5170
  severity: "warn",
@@ -4717,6 +5196,14 @@ function buildDoctorReport(root, args) {
4717
5196
  nextCommand: projectPath ? `decantr codify --from-audit --project ${projectPath}` : "decantr codify --from-audit"
4718
5197
  });
4719
5198
  }
5199
+ if (adoptionMode === "style-bridge" && !styleBridgePresent) {
5200
+ issues.push({
5201
+ category: "local-law",
5202
+ severity: "warn",
5203
+ message: "This app declares style-bridge adoption, but .decantr/style-bridge.json is missing.",
5204
+ nextCommand: styleBridgeProposalPresent ? projectPath ? `decantr codify --accept --project ${projectPath}` : "decantr codify --accept" : projectPath ? `decantr codify --style-bridge --project ${projectPath}` : "decantr codify --style-bridge"
5205
+ });
5206
+ }
4720
5207
  if ((essenceVersion === "4.0.0" || workspaceMode && projects.length > 0) && ciFiles.length === 0) {
4721
5208
  issues.push({
4722
5209
  category: "ci",
@@ -4744,6 +5231,7 @@ function buildDoctorReport(root, args) {
4744
5231
  adoptionMode,
4745
5232
  localPatternsPresent,
4746
5233
  localRulesPresent,
5234
+ styleBridgePresent,
4747
5235
  designAuthority,
4748
5236
  packManifestPresent
4749
5237
  });
@@ -4765,6 +5253,12 @@ function buildDoctorReport(root, args) {
4765
5253
  appendUnique(recommendedNextCommands, `decantr codify --from-audit${projectFlag}`);
4766
5254
  appendUnique(recommendedNextCommands, `decantr codify --accept${projectFlag}`);
4767
5255
  }
5256
+ if (adoptionMode === "style-bridge" && !styleBridgePresent) {
5257
+ appendUnique(
5258
+ recommendedNextCommands,
5259
+ styleBridgeProposalPresent ? `decantr codify --accept${projectFlag}` : `decantr codify --style-bridge${projectFlag}`
5260
+ );
5261
+ }
4768
5262
  appendUnique(
4769
5263
  recommendedNextCommands,
4770
5264
  issues.find((issue) => issue.category === "generated-artifact")?.nextCommand
@@ -4804,18 +5298,20 @@ function buildDoctorReport(root, args) {
4804
5298
  },
4805
5299
  generatedArtifacts: {
4806
5300
  contextDirPresent: workspaceMode ? projects.some(
4807
- (project) => existsSync16(join18(workspaceRoot, project.path, ".decantr", "context"))
4808
- ) : existsSync16(contextDir),
5301
+ (project) => existsSync17(join19(workspaceRoot, project.path, ".decantr", "context"))
5302
+ ) : existsSync17(contextDir),
4809
5303
  packManifestPresent: workspaceMode ? projects.length > 0 && workspaceProjectsMissingManifest.length === 0 : packManifestPresent,
4810
5304
  reviewPackPresent: workspaceMode ? projects.length > 0 && workspaceProjectsMissingReviewPack.length === 0 : reviewPackPresent,
4811
5305
  missingReferencedFiles: missingPackReferences.slice(0, 25)
4812
5306
  },
4813
5307
  localLaw: {
4814
5308
  patternsPresent: localPatternsPresent,
4815
- rulesPresent: localRulesPresent
5309
+ rulesPresent: localRulesPresent,
5310
+ styleBridgePresent,
5311
+ styleBridgeProposalPresent
4816
5312
  },
4817
5313
  visualEvidence: {
4818
- manifestPresent: existsSync16(join18(appRoot, ".decantr", "evidence", "visual-manifest.json"))
5314
+ manifestPresent: existsSync17(join19(appRoot, ".decantr", "evidence", "visual-manifest.json"))
4819
5315
  },
4820
5316
  lane,
4821
5317
  designAuthority,
@@ -4863,6 +5359,7 @@ function formatDoctorText(report) {
4863
5359
  `${BOLD3}Local Law:${RESET4}`,
4864
5360
  ` Local patterns: ${report.localLaw.patternsPresent ? "present" : "missing"}`,
4865
5361
  ` Local rules: ${report.localLaw.rulesPresent ? "present" : "missing"}`,
5362
+ ` Style bridge: ${report.localLaw.styleBridgePresent ? "present" : report.localLaw.styleBridgeProposalPresent ? "proposal pending" : "missing"}`,
4866
5363
  "",
4867
5364
  `${BOLD3}Workspace:${RESET4}`,
4868
5365
  ` Attached projects: ${report.workspace.attachedProjects.length}`,
@@ -4922,17 +5419,17 @@ async function cmdDoctor(args = ["doctor"], root = process.cwd()) {
4922
5419
  }
4923
5420
 
4924
5421
  // src/commands/export.ts
4925
- import { existsSync as existsSync17, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "fs";
4926
- import { dirname as dirname5, isAbsolute, join as join19 } from "path";
5422
+ import { existsSync as existsSync18, mkdirSync as mkdirSync12, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "fs";
5423
+ import { dirname as dirname5, isAbsolute, join as join20 } from "path";
4927
5424
  var GREEN5 = "\x1B[32m";
4928
5425
  var RED4 = "\x1B[31m";
4929
5426
  var DIM5 = "\x1B[2m";
4930
5427
  var RESET5 = "\x1B[0m";
4931
5428
  function readAdoptionMode(projectRoot) {
4932
- const projectJsonPath = join19(projectRoot, ".decantr", "project.json");
4933
- if (!existsSync17(projectJsonPath)) return null;
5429
+ const projectJsonPath = join20(projectRoot, ".decantr", "project.json");
5430
+ if (!existsSync18(projectJsonPath)) return null;
4934
5431
  try {
4935
- const parsed = JSON.parse(readFileSync14(projectJsonPath, "utf-8"));
5432
+ const parsed = JSON.parse(readFileSync15(projectJsonPath, "utf-8"));
4936
5433
  return typeof parsed.initialized?.adoptionMode === "string" ? parsed.initialized.adoptionMode : null;
4937
5434
  } catch {
4938
5435
  return null;
@@ -4940,7 +5437,7 @@ function readAdoptionMode(projectRoot) {
4940
5437
  }
4941
5438
  function resolveOutputPath(projectRoot, output, fallback) {
4942
5439
  if (!output) return fallback;
4943
- return isAbsolute(output) ? output : join19(projectRoot, output);
5440
+ return isAbsolute(output) ? output : join20(projectRoot, output);
4944
5441
  }
4945
5442
  var SHADCN_MAP = {
4946
5443
  "--d-bg": "--background",
@@ -5101,21 +5598,21 @@ function generateFigmaTokens(tokens) {
5101
5598
  `;
5102
5599
  }
5103
5600
  async function cmdExport(target, projectRoot, options = {}) {
5104
- const essencePath = join19(projectRoot, "decantr.essence.json");
5105
- const tokensPath = join19(projectRoot, "src", "styles", "tokens.css");
5106
- if (!existsSync17(essencePath)) {
5601
+ const essencePath = join20(projectRoot, "decantr.essence.json");
5602
+ const tokensPath = join20(projectRoot, "src", "styles", "tokens.css");
5603
+ if (!existsSync18(essencePath)) {
5107
5604
  console.error(`${RED4}No decantr.essence.json found. Run \`decantr init\` first.${RESET5}`);
5108
5605
  process.exitCode = 1;
5109
5606
  return;
5110
5607
  }
5111
- if (!existsSync17(tokensPath)) {
5608
+ if (!existsSync18(tokensPath)) {
5112
5609
  const adoptionMode = readAdoptionMode(projectRoot);
5113
5610
  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.";
5114
5611
  console.error(`${RED4}${message}${RESET5}`);
5115
5612
  process.exitCode = 1;
5116
5613
  return;
5117
5614
  }
5118
- const tokensCSS = readFileSync14(tokensPath, "utf-8");
5615
+ const tokensCSS = readFileSync15(tokensPath, "utf-8");
5119
5616
  const tokens = parseTokensCSS(tokensCSS);
5120
5617
  if (tokens.size === 0) {
5121
5618
  console.error(`${RED4}No --d-* tokens found in tokens.css.${RESET5}`);
@@ -5127,12 +5624,12 @@ async function cmdExport(target, projectRoot, options = {}) {
5127
5624
  const cssOut = resolveOutputPath(
5128
5625
  projectRoot,
5129
5626
  options.output,
5130
- join19(projectRoot, "src", "styles", "shadcn-theme.css")
5627
+ join20(projectRoot, "src", "styles", "shadcn-theme.css")
5131
5628
  );
5132
- const jsonOut = join19(projectRoot, "components.json");
5629
+ const jsonOut = join20(projectRoot, "components.json");
5133
5630
  ensureDir(cssOut);
5134
- writeFileSync12(cssOut, generateShadcnCSS(tokens), "utf-8");
5135
- writeFileSync12(jsonOut, generateShadcnComponentsJSON(), "utf-8");
5631
+ writeFileSync13(cssOut, generateShadcnCSS(tokens), "utf-8");
5632
+ writeFileSync13(jsonOut, generateShadcnComponentsJSON(), "utf-8");
5136
5633
  console.log(`${GREEN5}Exported shadcn theme:${RESET5}`);
5137
5634
  console.log(` ${DIM5}CSS:${RESET5} ${cssOut}`);
5138
5635
  console.log(` ${DIM5}JSON:${RESET5} ${jsonOut}`);
@@ -5142,10 +5639,10 @@ async function cmdExport(target, projectRoot, options = {}) {
5142
5639
  const out = resolveOutputPath(
5143
5640
  projectRoot,
5144
5641
  options.output,
5145
- join19(projectRoot, "tailwind.decantr.config.ts")
5642
+ join20(projectRoot, "tailwind.decantr.config.ts")
5146
5643
  );
5147
5644
  ensureDir(out);
5148
- writeFileSync12(out, generateTailwindConfig(tokens), "utf-8");
5645
+ writeFileSync13(out, generateTailwindConfig(tokens), "utf-8");
5149
5646
  console.log(`${GREEN5}Exported Tailwind config:${RESET5}`);
5150
5647
  console.log(` ${DIM5}File:${RESET5} ${out}`);
5151
5648
  break;
@@ -5154,10 +5651,10 @@ async function cmdExport(target, projectRoot, options = {}) {
5154
5651
  const out = resolveOutputPath(
5155
5652
  projectRoot,
5156
5653
  options.output,
5157
- join19(projectRoot, "decantr-tokens.css")
5654
+ join20(projectRoot, "decantr-tokens.css")
5158
5655
  );
5159
5656
  ensureDir(out);
5160
- writeFileSync12(out, generateCSSVars(tokens), "utf-8");
5657
+ writeFileSync13(out, generateCSSVars(tokens), "utf-8");
5161
5658
  console.log(`${GREEN5}Exported CSS variables:${RESET5}`);
5162
5659
  console.log(` ${DIM5}File:${RESET5} ${out}`);
5163
5660
  break;
@@ -5166,10 +5663,10 @@ async function cmdExport(target, projectRoot, options = {}) {
5166
5663
  const out = resolveOutputPath(
5167
5664
  projectRoot,
5168
5665
  options.output,
5169
- join19(projectRoot, ".decantr", "design", "figma-tokens.json")
5666
+ join20(projectRoot, ".decantr", "design", "figma-tokens.json")
5170
5667
  );
5171
5668
  ensureDir(out);
5172
- writeFileSync12(out, generateFigmaTokens(tokens), "utf-8");
5669
+ writeFileSync13(out, generateFigmaTokens(tokens), "utf-8");
5173
5670
  console.log(`${GREEN5}Exported Figma/Tokens Studio tokens:${RESET5}`);
5174
5671
  console.log(` ${DIM5}File:${RESET5} ${out}`);
5175
5672
  break;
@@ -5178,15 +5675,15 @@ async function cmdExport(target, projectRoot, options = {}) {
5178
5675
  }
5179
5676
  function ensureDir(filePath) {
5180
5677
  const dir = dirname5(filePath);
5181
- if (!existsSync17(dir)) {
5182
- mkdirSync11(dir, { recursive: true });
5678
+ if (!existsSync18(dir)) {
5679
+ mkdirSync12(dir, { recursive: true });
5183
5680
  }
5184
5681
  }
5185
5682
 
5186
5683
  // src/commands/magic.ts
5187
- import { existsSync as existsSync18 } from "fs";
5684
+ import { existsSync as existsSync19 } from "fs";
5188
5685
  import * as fs from "fs/promises";
5189
- import { join as join20 } from "path";
5686
+ import { join as join21 } from "path";
5190
5687
  var BOLD4 = "\x1B[1m";
5191
5688
  var DIM6 = "\x1B[2m";
5192
5689
  var RESET6 = "\x1B[0m";
@@ -5410,8 +5907,8 @@ async function cmdMagic(prompt, projectRoot, options) {
5410
5907
  console.log(` Archetype: ${intent.archetype}`);
5411
5908
  }
5412
5909
  console.log("");
5413
- const essencePath = join20(projectRoot, "decantr.essence.json");
5414
- if (existsSync18(essencePath)) {
5910
+ const essencePath = join21(projectRoot, "decantr.essence.json");
5911
+ if (existsSync19(essencePath)) {
5415
5912
  const projectFlag = options.projectLabel ? ` --project ${options.projectLabel}` : "";
5416
5913
  console.log(`${YELLOW4} Decantr is already attached to this project.${RESET6}`);
5417
5914
  console.log(
@@ -5444,7 +5941,7 @@ async function cmdMagic(prompt, projectRoot, options) {
5444
5941
  return;
5445
5942
  }
5446
5943
  const registryClient = new RegistryClient({
5447
- cacheDir: join20(projectRoot, ".decantr", "cache"),
5944
+ cacheDir: join21(projectRoot, ".decantr", "cache"),
5448
5945
  apiUrl: options.registry,
5449
5946
  offline: options.offline
5450
5947
  });
@@ -5715,14 +6212,14 @@ async function cmdMagic(prompt, projectRoot, options) {
5715
6212
  if (result.gitignoreUpdated) {
5716
6213
  console.log(` ${dim(".gitignore updated")}`);
5717
6214
  }
5718
- const contextDir = join20(projectRoot, ".decantr", "context");
6215
+ const contextDir = join21(projectRoot, ".decantr", "context");
5719
6216
  let sectionCount = 0;
5720
6217
  try {
5721
6218
  const files = await fs.readdir(contextDir);
5722
6219
  sectionCount = files.filter((f) => f.startsWith("section-")).length;
5723
6220
  } catch {
5724
6221
  }
5725
- const treatmentsPath = join20(projectRoot, "src", "styles", "treatments.css");
6222
+ const treatmentsPath = join21(projectRoot, "src", "styles", "treatments.css");
5726
6223
  let hasLayers = false;
5727
6224
  try {
5728
6225
  const css = await fs.readFile(treatmentsPath, "utf-8");
@@ -5755,8 +6252,8 @@ ${GREEN6}${BOLD4}Quality summary:${RESET6}`);
5755
6252
  }
5756
6253
 
5757
6254
  // src/commands/migrate.ts
5758
- import { copyFileSync, existsSync as existsSync19, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "fs";
5759
- import { join as join21 } from "path";
6255
+ import { copyFileSync, existsSync as existsSync20, readFileSync as readFileSync16, writeFileSync as writeFileSync14 } from "fs";
6256
+ import { join as join22 } from "path";
5760
6257
  import {
5761
6258
  isV4 as isV45,
5762
6259
  migrateToV4,
@@ -5769,12 +6266,12 @@ var YELLOW5 = "\x1B[33m";
5769
6266
  var RESET7 = "\x1B[0m";
5770
6267
  var DIM7 = "\x1B[2m";
5771
6268
  function migrateEssenceFile(essencePath) {
5772
- if (!existsSync19(essencePath)) {
6269
+ if (!existsSync20(essencePath)) {
5773
6270
  return { success: false, error: `File not found: ${essencePath}` };
5774
6271
  }
5775
6272
  let raw;
5776
6273
  try {
5777
- raw = readFileSync15(essencePath, "utf-8");
6274
+ raw = readFileSync16(essencePath, "utf-8");
5778
6275
  } catch (e) {
5779
6276
  return { success: false, error: `Could not read ${essencePath}: ${e.message}` };
5780
6277
  }
@@ -5815,7 +6312,7 @@ function migrateEssenceFile(essencePath) {
5815
6312
  };
5816
6313
  }
5817
6314
  try {
5818
- writeFileSync13(essencePath, JSON.stringify(v4, null, 2) + "\n");
6315
+ writeFileSync14(essencePath, JSON.stringify(v4, null, 2) + "\n");
5819
6316
  } catch (e) {
5820
6317
  return {
5821
6318
  success: false,
@@ -5836,8 +6333,8 @@ async function cmdMigrate(projectRoot = process.cwd(), args = []) {
5836
6333
  process.exitCode = 1;
5837
6334
  return;
5838
6335
  }
5839
- const essencePath = join21(projectRoot, "decantr.essence.json");
5840
- if (!existsSync19(essencePath)) {
6336
+ const essencePath = join22(projectRoot, "decantr.essence.json");
6337
+ if (!existsSync20(essencePath)) {
5841
6338
  console.error(`${RED5}No decantr.essence.json found. Run \`decantr init\` first.${RESET7}`);
5842
6339
  process.exitCode = 1;
5843
6340
  return;
@@ -5859,7 +6356,7 @@ async function cmdMigrate(projectRoot = process.cwd(), args = []) {
5859
6356
  }
5860
6357
  if (result.essence) {
5861
6358
  const registryClient = new RegistryClient({
5862
- cacheDir: join21(projectRoot, ".decantr", "cache")
6359
+ cacheDir: join22(projectRoot, ".decantr", "cache")
5863
6360
  });
5864
6361
  await refreshDerivedFiles(projectRoot, result.essence, registryClient);
5865
6362
  console.log(`${GREEN7}Derived context and execution packs refreshed.${RESET7}`);
@@ -5870,50 +6367,50 @@ async function cmdMigrate(projectRoot = process.cwd(), args = []) {
5870
6367
 
5871
6368
  // src/commands/new-project.ts
5872
6369
  import { spawnSync } from "child_process";
5873
- import { existsSync as existsSync21, mkdirSync as mkdirSync13 } from "fs";
5874
- import { join as join23, resolve as resolve3 } from "path";
6370
+ import { existsSync as existsSync22, mkdirSync as mkdirSync14 } from "fs";
6371
+ import { join as join24, resolve as resolve3 } from "path";
5875
6372
  import { fileURLToPath as fileURLToPath2 } from "url";
5876
6373
 
5877
6374
  // src/offline-content.ts
5878
- import { cpSync, existsSync as existsSync20, mkdirSync as mkdirSync12 } from "fs";
5879
- import { join as join22, resolve as resolve2 } from "path";
6375
+ import { cpSync, existsSync as existsSync21, mkdirSync as mkdirSync13 } from "fs";
6376
+ import { join as join23, resolve as resolve2 } from "path";
5880
6377
  var CONTENT_TYPES2 = ["archetypes", "blueprints", "patterns", "themes", "shells"];
5881
6378
  function copyIfExists(source, target) {
5882
- if (!existsSync20(source)) return false;
6379
+ if (!existsSync21(source)) return false;
5883
6380
  if (resolve2(source) === resolve2(target)) return true;
5884
6381
  cpSync(source, target, { recursive: true });
5885
6382
  return true;
5886
6383
  }
5887
6384
  function hydrateContentRoot(projectDir, contentRoot) {
5888
- if (!existsSync20(contentRoot)) return false;
5889
- const customRoot = join22(projectDir, ".decantr", "custom");
5890
- const cacheRoot = join22(projectDir, ".decantr", "cache", "@official");
5891
- mkdirSync12(customRoot, { recursive: true });
5892
- mkdirSync12(cacheRoot, { recursive: true });
6385
+ if (!existsSync21(contentRoot)) return false;
6386
+ const customRoot = join23(projectDir, ".decantr", "custom");
6387
+ const cacheRoot = join23(projectDir, ".decantr", "cache", "@official");
6388
+ mkdirSync13(customRoot, { recursive: true });
6389
+ mkdirSync13(cacheRoot, { recursive: true });
5893
6390
  let copiedAny = false;
5894
6391
  for (const type of CONTENT_TYPES2) {
5895
- const sourceDir = join22(contentRoot, type);
5896
- if (!existsSync20(sourceDir)) continue;
5897
- cpSync(sourceDir, join22(customRoot, type), { recursive: true });
5898
- cpSync(sourceDir, join22(cacheRoot, type), { recursive: true });
6392
+ const sourceDir = join23(contentRoot, type);
6393
+ if (!existsSync21(sourceDir)) continue;
6394
+ cpSync(sourceDir, join23(customRoot, type), { recursive: true });
6395
+ cpSync(sourceDir, join23(cacheRoot, type), { recursive: true });
5899
6396
  copiedAny = true;
5900
6397
  }
5901
6398
  return copiedAny;
5902
6399
  }
5903
6400
  function seedOfflineRegistry(projectDir, workspaceRoot) {
5904
- const projectDecantrRoot = join22(projectDir, ".decantr");
5905
- mkdirSync12(projectDecantrRoot, { recursive: true });
6401
+ const projectDecantrRoot = join23(projectDir, ".decantr");
6402
+ mkdirSync13(projectDecantrRoot, { recursive: true });
5906
6403
  const configuredContentRoot = process.env.DECANTR_CONTENT_DIR ? resolve2(process.env.DECANTR_CONTENT_DIR) : null;
5907
6404
  if (configuredContentRoot && hydrateContentRoot(projectDir, configuredContentRoot)) {
5908
6405
  return { seeded: true, strategy: "configured-content-root" };
5909
6406
  }
5910
6407
  const copiedCache = copyIfExists(
5911
- join22(workspaceRoot, ".decantr", "cache"),
5912
- join22(projectDecantrRoot, "cache")
6408
+ join23(workspaceRoot, ".decantr", "cache"),
6409
+ join23(projectDecantrRoot, "cache")
5913
6410
  );
5914
6411
  const copiedCustom = copyIfExists(
5915
- join22(workspaceRoot, ".decantr", "custom"),
5916
- join22(projectDecantrRoot, "custom")
6412
+ join23(workspaceRoot, ".decantr", "custom"),
6413
+ join23(projectDecantrRoot, "custom")
5917
6414
  );
5918
6415
  if (copiedCache || copiedCustom) {
5919
6416
  return { seeded: true, strategy: "workspace-cache" };
@@ -6026,7 +6523,7 @@ function runArgvCommand(command, args, cwd) {
6026
6523
  }
6027
6524
  function resolveInitCommand(initFlags) {
6028
6525
  const bundledCliEntrypoint = fileURLToPath2(new URL("./bin.js", import.meta.url));
6029
- const cliEntrypoint = existsSync21(bundledCliEntrypoint) ? bundledCliEntrypoint : process.argv[1] && existsSync21(process.argv[1]) ? process.argv[1] : null;
6526
+ const cliEntrypoint = existsSync22(bundledCliEntrypoint) ? bundledCliEntrypoint : process.argv[1] && existsSync22(process.argv[1]) ? process.argv[1] : null;
6030
6527
  if (cliEntrypoint) {
6031
6528
  return {
6032
6529
  command: process.execPath,
@@ -6054,7 +6551,7 @@ async function cmdNewProject(projectName, options) {
6054
6551
  process.exitCode = 1;
6055
6552
  return;
6056
6553
  }
6057
- if (existsSync21(projectDir)) {
6554
+ if (existsSync22(projectDir)) {
6058
6555
  console.error(error(`Directory "${projectName}" already exists.`));
6059
6556
  process.exitCode = 1;
6060
6557
  return;
@@ -6068,7 +6565,7 @@ async function cmdNewProject(projectName, options) {
6068
6565
  return;
6069
6566
  }
6070
6567
  console.log(heading(`Creating ${projectName}...`));
6071
- mkdirSync13(projectDir, { recursive: true });
6568
+ mkdirSync14(projectDir, { recursive: true });
6072
6569
  console.log(dim2(` Created ${projectName}/`));
6073
6570
  const title = projectName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
6074
6571
  if (shouldBootstrapRuntime && bootstrapAdapter) {
@@ -6167,21 +6664,21 @@ ${YELLOW6}Decantr init encountered issues. Run \`decantr init\` manually inside
6167
6664
  }
6168
6665
  }
6169
6666
  function detectPackageManager4() {
6170
- if (existsSync21(join23(process.cwd(), "pnpm-lock.yaml")) || existsSync21(join23(process.cwd(), "pnpm-workspace.yaml"))) {
6667
+ if (existsSync22(join24(process.cwd(), "pnpm-lock.yaml")) || existsSync22(join24(process.cwd(), "pnpm-workspace.yaml"))) {
6171
6668
  return "pnpm";
6172
6669
  }
6173
- if (existsSync21(join23(process.cwd(), "yarn.lock"))) {
6670
+ if (existsSync22(join24(process.cwd(), "yarn.lock"))) {
6174
6671
  return "yarn";
6175
6672
  }
6176
- if (existsSync21(join23(process.cwd(), "bun.lockb")) || existsSync21(join23(process.cwd(), "bun.lock"))) {
6673
+ if (existsSync22(join24(process.cwd(), "bun.lockb")) || existsSync22(join24(process.cwd(), "bun.lock"))) {
6177
6674
  return "bun";
6178
6675
  }
6179
6676
  return "npm";
6180
6677
  }
6181
6678
 
6182
6679
  // src/commands/publish.ts
6183
- import { existsSync as existsSync22, readFileSync as readFileSync16 } from "fs";
6184
- import { join as join24 } from "path";
6680
+ import { existsSync as existsSync23, readFileSync as readFileSync17 } from "fs";
6681
+ import { join as join25 } from "path";
6185
6682
  import {
6186
6683
  API_CONTENT_TYPE_TO_CONTENT_TYPE,
6187
6684
  CONTENT_TYPE_TO_API_CONTENT_TYPE as CONTENT_TYPE_TO_API_CONTENT_TYPE2,
@@ -6197,8 +6694,8 @@ async function cmdPublish(type, name, projectRoot = process.cwd()) {
6197
6694
  }
6198
6695
  const singularType = API_CONTENT_TYPE_TO_CONTENT_TYPE[type] || type;
6199
6696
  const pluralType = CONTENT_TYPE_TO_API_CONTENT_TYPE2[type] || CONTENT_TYPE_TO_API_CONTENT_TYPE2[singularType] || `${type}s`;
6200
- const customPath = join24(projectRoot, ".decantr", "custom", pluralType, `${name}.json`);
6201
- if (!existsSync22(customPath)) {
6697
+ const customPath = join25(projectRoot, ".decantr", "custom", pluralType, `${name}.json`);
6698
+ if (!existsSync23(customPath)) {
6202
6699
  console.error(`Custom ${singularType} "${name}" not found at ${customPath}`);
6203
6700
  console.error(`Create one first: decantr create ${singularType} ${name}`);
6204
6701
  process.exitCode = 1;
@@ -6206,7 +6703,7 @@ async function cmdPublish(type, name, projectRoot = process.cwd()) {
6206
6703
  }
6207
6704
  let data;
6208
6705
  try {
6209
- data = JSON.parse(readFileSync16(customPath, "utf-8"));
6706
+ data = JSON.parse(readFileSync17(customPath, "utf-8"));
6210
6707
  } catch {
6211
6708
  console.error(`Failed to parse ${customPath}`);
6212
6709
  process.exitCode = 1;
@@ -6245,8 +6742,8 @@ async function cmdPublish(type, name, projectRoot = process.cwd()) {
6245
6742
 
6246
6743
  // src/commands/refresh.ts
6247
6744
  import { createHash } from "crypto";
6248
- import { existsSync as existsSync23, readdirSync as readdirSync7, readFileSync as readFileSync17, statSync as statSync6 } from "fs";
6249
- import { isAbsolute as isAbsolute2, join as join25, relative as relative6 } from "path";
6745
+ import { existsSync as existsSync24, readdirSync as readdirSync7, readFileSync as readFileSync18, statSync as statSync6 } from "fs";
6746
+ import { isAbsolute as isAbsolute2, join as join26, relative as relative6 } from "path";
6250
6747
  import { collectMissingPackManifestFiles as collectMissingPackManifestFiles2 } from "@decantr/verifier";
6251
6748
  import { isV4 as isV46 } from "@decantr/essence-spec";
6252
6749
  var GREEN9 = "\x1B[32m";
@@ -6254,13 +6751,13 @@ var RED7 = "\x1B[31m";
6254
6751
  var DIM9 = "\x1B[2m";
6255
6752
  var RESET9 = "\x1B[0m";
6256
6753
  function hashFile(path) {
6257
- return createHash("sha256").update(readFileSync17(path)).digest("hex");
6754
+ return createHash("sha256").update(readFileSync18(path)).digest("hex");
6258
6755
  }
6259
6756
  function walkFiles(dir) {
6260
- if (!existsSync23(dir)) return [];
6757
+ if (!existsSync24(dir)) return [];
6261
6758
  const files = [];
6262
6759
  for (const entry of readdirSync7(dir, { withFileTypes: true })) {
6263
- const path = join25(dir, entry.name);
6760
+ const path = join26(dir, entry.name);
6264
6761
  if (entry.isDirectory()) {
6265
6762
  files.push(...walkFiles(path));
6266
6763
  } else if (entry.isFile()) {
@@ -6271,10 +6768,10 @@ function walkFiles(dir) {
6271
6768
  }
6272
6769
  function trackedGeneratedFiles(projectRoot) {
6273
6770
  return [
6274
- join25(projectRoot, "DECANTR.md"),
6275
- ...walkFiles(join25(projectRoot, ".decantr", "context")),
6276
- ...["global.css", "tokens.css", "treatments.css", "decantr-bridge.css"].map((file) => join25(projectRoot, "src", "styles", file)).filter((path) => existsSync23(path))
6277
- ].filter((path) => existsSync23(path));
6771
+ join26(projectRoot, "DECANTR.md"),
6772
+ ...walkFiles(join26(projectRoot, ".decantr", "context")),
6773
+ ...["global.css", "tokens.css", "treatments.css", "decantr-bridge.css"].map((file) => join26(projectRoot, "src", "styles", file)).filter((path) => existsSync24(path))
6774
+ ].filter((path) => existsSync24(path));
6278
6775
  }
6279
6776
  function snapshotGeneratedFiles(projectRoot) {
6280
6777
  const map = /* @__PURE__ */ new Map();
@@ -6297,33 +6794,33 @@ function fileMtimeMs(path) {
6297
6794
  }
6298
6795
  function newestInputMtime(projectRoot) {
6299
6796
  return Math.max(
6300
- fileMtimeMs(join25(projectRoot, "decantr.essence.json")),
6301
- fileMtimeMs(join25(projectRoot, ".decantr", "project.json"))
6797
+ fileMtimeMs(join26(projectRoot, "decantr.essence.json")),
6798
+ fileMtimeMs(join26(projectRoot, ".decantr", "project.json"))
6302
6799
  );
6303
6800
  }
6304
6801
  function isContractOnlyProject2(projectRoot) {
6305
- const projectJsonPath = join25(projectRoot, ".decantr", "project.json");
6306
- if (!existsSync23(projectJsonPath)) return false;
6802
+ const projectJsonPath = join26(projectRoot, ".decantr", "project.json");
6803
+ if (!existsSync24(projectJsonPath)) return false;
6307
6804
  try {
6308
- const projectJson = JSON.parse(readFileSync17(projectJsonPath, "utf-8"));
6805
+ const projectJson = JSON.parse(readFileSync18(projectJsonPath, "utf-8"));
6309
6806
  return projectJson.initialized?.adoptionMode === "contract-only";
6310
6807
  } catch {
6311
6808
  return false;
6312
6809
  }
6313
6810
  }
6314
6811
  function checkRefreshFreshness(projectRoot) {
6315
- const contextDir = join25(projectRoot, ".decantr", "context");
6812
+ const contextDir = join26(projectRoot, ".decantr", "context");
6316
6813
  const generated = trackedGeneratedFiles(projectRoot);
6317
6814
  const reasons = [];
6318
6815
  const packHydrationOptional = isContractOnlyProject2(projectRoot);
6319
- if (!existsSync23(join25(projectRoot, "DECANTR.md"))) reasons.push("DECANTR.md is missing.");
6320
- if (!existsSync23(contextDir)) reasons.push(".decantr/context is missing.");
6321
- if (!existsSync23(join25(contextDir, "scaffold.md"))) {
6816
+ if (!existsSync24(join26(projectRoot, "DECANTR.md"))) reasons.push("DECANTR.md is missing.");
6817
+ if (!existsSync24(contextDir)) reasons.push(".decantr/context is missing.");
6818
+ if (!existsSync24(join26(contextDir, "scaffold.md"))) {
6322
6819
  reasons.push(".decantr/context/scaffold.md is missing.");
6323
6820
  }
6324
- if (!packHydrationOptional && !existsSync23(join25(contextDir, "pack-manifest.json"))) {
6821
+ if (!packHydrationOptional && !existsSync24(join26(contextDir, "pack-manifest.json"))) {
6325
6822
  reasons.push(".decantr/context/pack-manifest.json is missing.");
6326
- } else if (existsSync23(join25(contextDir, "pack-manifest.json"))) {
6823
+ } else if (existsSync24(join26(contextDir, "pack-manifest.json"))) {
6327
6824
  const missingPackFiles = collectMissingPackManifestFiles2(projectRoot);
6328
6825
  if (missingPackFiles.length > 0) {
6329
6826
  reasons.push(
@@ -6376,7 +6873,7 @@ function summarizeChanges(projectRoot, before, after) {
6376
6873
  }
6377
6874
  function displayGeneratedPath(summary, file, displayRoot) {
6378
6875
  if (!displayRoot) return file;
6379
- const absolutePath = join25(summary.projectRoot, file);
6876
+ const absolutePath = join26(summary.projectRoot, file);
6380
6877
  const relativePath = relative6(displayRoot, absolutePath).replace(/\\/g, "/");
6381
6878
  if (relativePath && !relativePath.startsWith("..") && !isAbsolute2(relativePath)) {
6382
6879
  return relativePath;
@@ -6411,15 +6908,15 @@ function printRefreshSummary(summary, displayRoot) {
6411
6908
  }
6412
6909
  }
6413
6910
  async function cmdRefresh(projectRoot = process.cwd(), options = {}) {
6414
- const essencePath = join25(projectRoot, "decantr.essence.json");
6415
- if (!existsSync23(essencePath)) {
6911
+ const essencePath = join26(projectRoot, "decantr.essence.json");
6912
+ if (!existsSync24(essencePath)) {
6416
6913
  console.error(`${RED7}No decantr.essence.json found. Run \`decantr init\` first.${RESET9}`);
6417
6914
  process.exitCode = 1;
6418
6915
  return;
6419
6916
  }
6420
6917
  let essence;
6421
6918
  try {
6422
- const raw = readFileSync17(essencePath, "utf-8");
6919
+ const raw = readFileSync18(essencePath, "utf-8");
6423
6920
  const parsed = JSON.parse(raw);
6424
6921
  if (!isV46(parsed)) {
6425
6922
  console.error(
@@ -6445,7 +6942,7 @@ async function cmdRefresh(projectRoot = process.cwd(), options = {}) {
6445
6942
  return;
6446
6943
  }
6447
6944
  const registryClient = new RegistryClient({
6448
- cacheDir: join25(projectRoot, ".decantr", "cache"),
6945
+ cacheDir: join26(projectRoot, ".decantr", "cache"),
6449
6946
  offline: options.offline
6450
6947
  });
6451
6948
  console.log("Regenerating derived files...\n");
@@ -6476,8 +6973,8 @@ async function cmdRefresh(projectRoot = process.cwd(), options = {}) {
6476
6973
  }
6477
6974
 
6478
6975
  // src/commands/registry-mirror.ts
6479
- import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync14 } from "fs";
6480
- import { join as join26 } from "path";
6976
+ import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync15 } from "fs";
6977
+ import { join as join27 } from "path";
6481
6978
  import { API_CONTENT_TYPES, RegistryAPIClient as RegistryAPIClient2 } from "@decantr/registry";
6482
6979
  var GREEN10 = "\x1B[32m";
6483
6980
  var RED8 = "\x1B[31m";
@@ -6505,7 +7002,7 @@ async function cmdRegistryMirror(projectRoot, options = {}) {
6505
7002
  process.exitCode = 1;
6506
7003
  return;
6507
7004
  }
6508
- const cacheDir = join26(projectRoot, ".decantr", "cache");
7005
+ const cacheDir = join27(projectRoot, ".decantr", "cache");
6509
7006
  const counts = {};
6510
7007
  const failed = [];
6511
7008
  console.log(`
@@ -6515,19 +7012,19 @@ Mirroring registry content to ${DIM10}.decantr/cache/${RESET10}
6515
7012
  try {
6516
7013
  const result = await apiClient.listContent(type, { namespace: "@official" });
6517
7014
  const items = result.items;
6518
- const typeDir = join26(cacheDir, "@official", type);
6519
- mkdirSync14(typeDir, { recursive: true });
6520
- writeFileSync14(join26(typeDir, "index.json"), JSON.stringify(result, null, 2));
7015
+ const typeDir = join27(cacheDir, "@official", type);
7016
+ mkdirSync15(typeDir, { recursive: true });
7017
+ writeFileSync15(join27(typeDir, "index.json"), JSON.stringify(result, null, 2));
6521
7018
  let itemCount = 0;
6522
7019
  for (const item of items) {
6523
7020
  const slug = item.slug || item.id;
6524
7021
  if (!slug) continue;
6525
7022
  try {
6526
7023
  const fullItem = await apiClient.getContent(type, "@official", slug);
6527
- writeFileSync14(join26(typeDir, `${slug}.json`), JSON.stringify(fullItem, null, 2));
7024
+ writeFileSync15(join27(typeDir, `${slug}.json`), JSON.stringify(fullItem, null, 2));
6528
7025
  itemCount++;
6529
7026
  } catch {
6530
- writeFileSync14(join26(typeDir, `${slug}.json`), JSON.stringify(item, null, 2));
7027
+ writeFileSync15(join27(typeDir, `${slug}.json`), JSON.stringify(item, null, 2));
6531
7028
  itemCount++;
6532
7029
  }
6533
7030
  }
@@ -6542,8 +7039,8 @@ Mirroring registry content to ${DIM10}.decantr/cache/${RESET10}
6542
7039
  mirrored_at: (/* @__PURE__ */ new Date()).toISOString(),
6543
7040
  counts
6544
7041
  };
6545
- mkdirSync14(join26(cacheDir), { recursive: true });
6546
- writeFileSync14(join26(cacheDir, "mirror-manifest.json"), JSON.stringify(manifest, null, 2));
7042
+ mkdirSync15(join27(cacheDir), { recursive: true });
7043
+ writeFileSync15(join27(cacheDir, "mirror-manifest.json"), JSON.stringify(manifest, null, 2));
6547
7044
  const totalItems = Object.values(counts).reduce((a, b) => a + b, 0);
6548
7045
  console.log("");
6549
7046
  if (failed.length > 0) {
@@ -6560,8 +7057,8 @@ Mirroring registry content to ${DIM10}.decantr/cache/${RESET10}
6560
7057
  }
6561
7058
 
6562
7059
  // src/commands/remove.ts
6563
- import { existsSync as existsSync25, readFileSync as readFileSync18, rmSync as rmSync2, writeFileSync as writeFileSync15 } from "fs";
6564
- import { join as join27 } from "path";
7060
+ import { existsSync as existsSync26, readFileSync as readFileSync19, rmSync as rmSync2, writeFileSync as writeFileSync16 } from "fs";
7061
+ import { join as join28 } from "path";
6565
7062
  import { isV4 as isV47 } from "@decantr/essence-spec";
6566
7063
  var GREEN11 = "\x1B[32m";
6567
7064
  var RED9 = "\x1B[31m";
@@ -6569,15 +7066,15 @@ var YELLOW8 = "\x1B[33m";
6569
7066
  var DIM11 = "\x1B[2m";
6570
7067
  var RESET11 = "\x1B[0m";
6571
7068
  function readV4Essence2(projectRoot) {
6572
- const essencePath = join27(projectRoot, "decantr.essence.json");
6573
- if (!existsSync25(essencePath)) {
7069
+ const essencePath = join28(projectRoot, "decantr.essence.json");
7070
+ if (!existsSync26(essencePath)) {
6574
7071
  console.error(`${RED9}No decantr.essence.json found. Run \`decantr init\` first.${RESET11}`);
6575
7072
  process.exitCode = 1;
6576
7073
  return null;
6577
7074
  }
6578
7075
  let parsed;
6579
7076
  try {
6580
- parsed = JSON.parse(readFileSync18(essencePath, "utf-8"));
7077
+ parsed = JSON.parse(readFileSync19(essencePath, "utf-8"));
6581
7078
  } catch (e) {
6582
7079
  console.error(`${RED9}Could not read essence: ${e.message}${RESET11}`);
6583
7080
  process.exitCode = 1;
@@ -6593,7 +7090,7 @@ function readV4Essence2(projectRoot) {
6593
7090
  return { essence: parsed, essencePath };
6594
7091
  }
6595
7092
  function writeEssence2(essencePath, essence) {
6596
- writeFileSync15(essencePath, JSON.stringify(essence, null, 2) + "\n");
7093
+ writeFileSync16(essencePath, JSON.stringify(essence, null, 2) + "\n");
6597
7094
  }
6598
7095
  function readFlagValue2(args, name) {
6599
7096
  const prefix = `--${name}=`;
@@ -6665,14 +7162,14 @@ async function cmdRemoveSection(sectionId, args, projectRoot = process.cwd()) {
6665
7162
  sections.splice(idx, 1);
6666
7163
  recomputeGlobalFeatures(essence);
6667
7164
  removeRoutes(essence, sectionId);
6668
- const contextFile = join27(projectRoot, ".decantr", "context", `${sectionId}.md`);
6669
- if (existsSync25(contextFile)) {
7165
+ const contextFile = join28(projectRoot, ".decantr", "context", `${sectionId}.md`);
7166
+ if (existsSync26(contextFile)) {
6670
7167
  rmSync2(contextFile);
6671
7168
  }
6672
7169
  writeEssence2(essencePath, essence);
6673
7170
  console.log(`${GREEN11}Removed section "${sectionId}".${RESET11}`);
6674
7171
  const registryClient = new RegistryClient({
6675
- cacheDir: join27(projectRoot, ".decantr", "cache")
7172
+ cacheDir: join28(projectRoot, ".decantr", "cache")
6676
7173
  });
6677
7174
  await refreshDerivedFiles(projectRoot, essence, registryClient);
6678
7175
  console.log(`${GREEN11}Derived files refreshed.${RESET11}`);
@@ -6715,7 +7212,7 @@ async function cmdRemovePage(path, args, projectRoot = process.cwd()) {
6715
7212
  }
6716
7213
  console.log(`${GREEN11}Removed page "${pageId}" from section "${resolvedSectionId}".${RESET11}`);
6717
7214
  const registryClient = new RegistryClient({
6718
- cacheDir: join27(projectRoot, ".decantr", "cache")
7215
+ cacheDir: join28(projectRoot, ".decantr", "cache")
6719
7216
  });
6720
7217
  await refreshDerivedFiles(projectRoot, essence, registryClient);
6721
7218
  console.log(`${GREEN11}Derived files refreshed.${RESET11}`);
@@ -6758,15 +7255,15 @@ async function cmdRemoveFeature(feature, args, projectRoot = process.cwd()) {
6758
7255
  const target = sectionId ? `section "${sectionId}" and global` : "global";
6759
7256
  console.log(`${GREEN11}Removed feature "${feature}" from ${target} features.${RESET11}`);
6760
7257
  const registryClient = new RegistryClient({
6761
- cacheDir: join27(projectRoot, ".decantr", "cache")
7258
+ cacheDir: join28(projectRoot, ".decantr", "cache")
6762
7259
  });
6763
7260
  await refreshDerivedFiles(projectRoot, essence, registryClient);
6764
7261
  console.log(`${GREEN11}Derived files refreshed.${RESET11}`);
6765
7262
  }
6766
7263
 
6767
7264
  // src/commands/sync-drift.ts
6768
- import { existsSync as existsSync26, readFileSync as readFileSync19, writeFileSync as writeFileSync16 } from "fs";
6769
- import { join as join28 } from "path";
7265
+ import { existsSync as existsSync27, readFileSync as readFileSync20, writeFileSync as writeFileSync17 } from "fs";
7266
+ import { join as join29 } from "path";
6770
7267
  var GREEN12 = "\x1B[32m";
6771
7268
  var RED10 = "\x1B[31m";
6772
7269
  var YELLOW9 = "\x1B[33m";
@@ -6775,8 +7272,8 @@ var DIM12 = "\x1B[2m";
6775
7272
  var BOLD6 = "\x1B[1m";
6776
7273
  var CYAN6 = "\x1B[36m";
6777
7274
  async function cmdSyncDrift(projectRoot = process.cwd()) {
6778
- const driftLogPath = join28(projectRoot, ".decantr", "drift-log.json");
6779
- if (!existsSync26(driftLogPath)) {
7275
+ const driftLogPath = join29(projectRoot, ".decantr", "drift-log.json");
7276
+ if (!existsSync27(driftLogPath)) {
6780
7277
  console.log(`${GREEN12}No drift log found \u2014 no drift recorded.${RESET12}`);
6781
7278
  console.log(
6782
7279
  `${DIM12}Drift is logged when guard violations are detected during development.${RESET12}`
@@ -6785,7 +7282,7 @@ async function cmdSyncDrift(projectRoot = process.cwd()) {
6785
7282
  }
6786
7283
  let entries;
6787
7284
  try {
6788
- entries = JSON.parse(readFileSync19(driftLogPath, "utf-8"));
7285
+ entries = JSON.parse(readFileSync20(driftLogPath, "utf-8"));
6789
7286
  } catch {
6790
7287
  console.error(`${RED10}Could not parse drift-log.json${RESET12}`);
6791
7288
  process.exitCode = 1;
@@ -6831,13 +7328,13 @@ ${BOLD6}Unresolved Drift Entries (${unresolved.length})${RESET12}
6831
7328
  console.log("");
6832
7329
  }
6833
7330
  function resolveDriftEntries(projectRoot, options) {
6834
- const driftLogPath = join28(projectRoot, ".decantr", "drift-log.json");
6835
- if (!existsSync26(driftLogPath)) {
7331
+ const driftLogPath = join29(projectRoot, ".decantr", "drift-log.json");
7332
+ if (!existsSync27(driftLogPath)) {
6836
7333
  return { success: true };
6837
7334
  }
6838
7335
  if (options.clear) {
6839
7336
  try {
6840
- writeFileSync16(driftLogPath, "[]");
7337
+ writeFileSync17(driftLogPath, "[]");
6841
7338
  return { success: true };
6842
7339
  } catch (e) {
6843
7340
  return { success: false, error: `Could not clear drift log: ${e.message}` };
@@ -6845,7 +7342,7 @@ function resolveDriftEntries(projectRoot, options) {
6845
7342
  }
6846
7343
  let entries;
6847
7344
  try {
6848
- entries = JSON.parse(readFileSync19(driftLogPath, "utf-8"));
7345
+ entries = JSON.parse(readFileSync20(driftLogPath, "utf-8"));
6849
7346
  } catch {
6850
7347
  return { success: false, error: "Could not parse drift-log.json" };
6851
7348
  }
@@ -6864,7 +7361,7 @@ function resolveDriftEntries(projectRoot, options) {
6864
7361
  }
6865
7362
  }
6866
7363
  try {
6867
- writeFileSync16(driftLogPath, JSON.stringify(entries, null, 2));
7364
+ writeFileSync17(driftLogPath, JSON.stringify(entries, null, 2));
6868
7365
  return { success: true };
6869
7366
  } catch (e) {
6870
7367
  return { success: false, error: `Could not write drift log: ${e.message}` };
@@ -7089,8 +7586,8 @@ function trimTrailingSlashes(value) {
7089
7586
  }
7090
7587
 
7091
7588
  // src/commands/theme-switch.ts
7092
- import { existsSync as existsSync27, readFileSync as readFileSync20, writeFileSync as writeFileSync17 } from "fs";
7093
- import { join as join29 } from "path";
7589
+ import { existsSync as existsSync28, readFileSync as readFileSync21, writeFileSync as writeFileSync18 } from "fs";
7590
+ import { join as join30 } from "path";
7094
7591
  import { isV4 as isV48 } from "@decantr/essence-spec";
7095
7592
  var GREEN14 = "\x1B[32m";
7096
7593
  var RED11 = "\x1B[31m";
@@ -7107,15 +7604,15 @@ async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
7107
7604
  process.exitCode = 1;
7108
7605
  return;
7109
7606
  }
7110
- const essencePath = join29(projectRoot, "decantr.essence.json");
7111
- if (!existsSync27(essencePath)) {
7607
+ const essencePath = join30(projectRoot, "decantr.essence.json");
7608
+ if (!existsSync28(essencePath)) {
7112
7609
  console.error(`${RED11}No decantr.essence.json found. Run \`decantr init\` first.${RESET14}`);
7113
7610
  process.exitCode = 1;
7114
7611
  return;
7115
7612
  }
7116
7613
  let parsed;
7117
7614
  try {
7118
- parsed = JSON.parse(readFileSync20(essencePath, "utf-8"));
7615
+ parsed = JSON.parse(readFileSync21(essencePath, "utf-8"));
7119
7616
  } catch (e) {
7120
7617
  console.error(`${RED11}Could not read essence: ${e.message}${RESET14}`);
7121
7618
  process.exitCode = 1;
@@ -7166,7 +7663,7 @@ async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
7166
7663
  essence.dna.theme.mode = mode;
7167
7664
  }
7168
7665
  const registryClient = new RegistryClient({
7169
- cacheDir: join29(projectRoot, ".decantr", "cache")
7666
+ cacheDir: join30(projectRoot, ".decantr", "cache")
7170
7667
  });
7171
7668
  try {
7172
7669
  const themeResult = await registryClient.fetchTheme(themeName);
@@ -7181,7 +7678,7 @@ async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
7181
7678
  }
7182
7679
  } catch {
7183
7680
  }
7184
- writeFileSync17(essencePath, JSON.stringify(essence, null, 2) + "\n");
7681
+ writeFileSync18(essencePath, JSON.stringify(essence, null, 2) + "\n");
7185
7682
  console.log(`${GREEN14}Switched theme: ${oldThemeId} \u2192 ${themeName}${RESET14}`);
7186
7683
  if (shape) console.log(` ${DIM14}Shape: ${shape}${RESET14}`);
7187
7684
  if (mode) console.log(` ${DIM14}Mode: ${mode}${RESET14}`);
@@ -7489,8 +7986,8 @@ async function runSimplifiedInit(blueprints) {
7489
7986
  }
7490
7987
 
7491
7988
  // src/theme-commands.ts
7492
- import { existsSync as existsSync28, mkdirSync as mkdirSync15, readdirSync as readdirSync8, readFileSync as readFileSync21, rmSync as rmSync3, writeFileSync as writeFileSync18 } from "fs";
7493
- import { join as join30 } from "path";
7989
+ import { existsSync as existsSync29, mkdirSync as mkdirSync16, readdirSync as readdirSync8, readFileSync as readFileSync22, rmSync as rmSync3, writeFileSync as writeFileSync19 } from "fs";
7990
+ import { join as join31 } from "path";
7494
7991
  var REQUIRED_FIELDS = [
7495
7992
  "$schema",
7496
7993
  "id",
@@ -7550,20 +8047,20 @@ function validateCustomTheme(theme) {
7550
8047
  };
7551
8048
  }
7552
8049
  function createTheme(projectRoot, id, name) {
7553
- const customThemesDir = join30(projectRoot, ".decantr", "custom", "themes");
7554
- const themePath = join30(customThemesDir, `${id}.json`);
7555
- const howToPath = join30(customThemesDir, "how-to-theme.md");
7556
- mkdirSync15(customThemesDir, { recursive: true });
7557
- if (existsSync28(themePath)) {
8050
+ const customThemesDir = join31(projectRoot, ".decantr", "custom", "themes");
8051
+ const themePath = join31(customThemesDir, `${id}.json`);
8052
+ const howToPath = join31(customThemesDir, "how-to-theme.md");
8053
+ mkdirSync16(customThemesDir, { recursive: true });
8054
+ if (existsSync29(themePath)) {
7558
8055
  return {
7559
8056
  success: false,
7560
8057
  error: `Theme "${id}" already exists at ${themePath}`
7561
8058
  };
7562
8059
  }
7563
8060
  const skeleton = getThemeSkeleton(id, name);
7564
- writeFileSync18(themePath, JSON.stringify(skeleton, null, 2));
7565
- if (!existsSync28(howToPath)) {
7566
- writeFileSync18(howToPath, getHowToThemeDoc());
8061
+ writeFileSync19(themePath, JSON.stringify(skeleton, null, 2));
8062
+ if (!existsSync29(howToPath)) {
8063
+ writeFileSync19(howToPath, getHowToThemeDoc());
7567
8064
  }
7568
8065
  return {
7569
8066
  success: true,
@@ -7571,17 +8068,17 @@ function createTheme(projectRoot, id, name) {
7571
8068
  };
7572
8069
  }
7573
8070
  function listCustomThemes(projectRoot) {
7574
- const customThemesDir = join30(projectRoot, ".decantr", "custom", "themes");
7575
- if (!existsSync28(customThemesDir)) {
8071
+ const customThemesDir = join31(projectRoot, ".decantr", "custom", "themes");
8072
+ if (!existsSync29(customThemesDir)) {
7576
8073
  return [];
7577
8074
  }
7578
8075
  const themes = [];
7579
8076
  try {
7580
8077
  const files = readdirSync8(customThemesDir).filter((f) => f.endsWith(".json"));
7581
8078
  for (const file of files) {
7582
- const filePath = join30(customThemesDir, file);
8079
+ const filePath = join31(customThemesDir, file);
7583
8080
  try {
7584
- const data = JSON.parse(readFileSync21(filePath, "utf-8"));
8081
+ const data = JSON.parse(readFileSync22(filePath, "utf-8"));
7585
8082
  themes.push({
7586
8083
  id: data.id || file.replace(".json", ""),
7587
8084
  name: data.name || data.id,
@@ -7596,8 +8093,8 @@ function listCustomThemes(projectRoot) {
7596
8093
  return themes;
7597
8094
  }
7598
8095
  function deleteTheme(projectRoot, id) {
7599
- const themePath = join30(projectRoot, ".decantr", "custom", "themes", `${id}.json`);
7600
- if (!existsSync28(themePath)) {
8096
+ const themePath = join31(projectRoot, ".decantr", "custom", "themes", `${id}.json`);
8097
+ if (!existsSync29(themePath)) {
7601
8098
  return {
7602
8099
  success: false,
7603
8100
  error: `Theme "${id}" not found at ${themePath}`
@@ -7614,7 +8111,7 @@ function deleteTheme(projectRoot, id) {
7614
8111
  }
7615
8112
  }
7616
8113
  function importTheme(projectRoot, sourcePath) {
7617
- if (!existsSync28(sourcePath)) {
8114
+ if (!existsSync29(sourcePath)) {
7618
8115
  return {
7619
8116
  success: false,
7620
8117
  errors: [`Source file not found: ${sourcePath}`]
@@ -7622,7 +8119,7 @@ function importTheme(projectRoot, sourcePath) {
7622
8119
  }
7623
8120
  let theme;
7624
8121
  try {
7625
- theme = JSON.parse(readFileSync21(sourcePath, "utf-8"));
8122
+ theme = JSON.parse(readFileSync22(sourcePath, "utf-8"));
7626
8123
  } catch (e) {
7627
8124
  return {
7628
8125
  success: false,
@@ -7638,14 +8135,14 @@ function importTheme(projectRoot, sourcePath) {
7638
8135
  }
7639
8136
  theme.source = "custom";
7640
8137
  const id = theme.id;
7641
- const customThemesDir = join30(projectRoot, ".decantr", "custom", "themes");
7642
- const destPath = join30(customThemesDir, `${id}.json`);
7643
- mkdirSync15(customThemesDir, { recursive: true });
7644
- const howToPath = join30(customThemesDir, "how-to-theme.md");
7645
- if (!existsSync28(howToPath)) {
7646
- writeFileSync18(howToPath, getHowToThemeDoc());
7647
- }
7648
- writeFileSync18(destPath, JSON.stringify(theme, null, 2));
8138
+ const customThemesDir = join31(projectRoot, ".decantr", "custom", "themes");
8139
+ const destPath = join31(customThemesDir, `${id}.json`);
8140
+ mkdirSync16(customThemesDir, { recursive: true });
8141
+ const howToPath = join31(customThemesDir, "how-to-theme.md");
8142
+ if (!existsSync29(howToPath)) {
8143
+ writeFileSync19(howToPath, getHowToThemeDoc());
8144
+ }
8145
+ writeFileSync19(destPath, JSON.stringify(theme, null, 2));
7649
8146
  return {
7650
8147
  success: true,
7651
8148
  path: destPath
@@ -8192,18 +8689,18 @@ function extractHostedAssetPaths(indexHtml) {
8192
8689
  return [...assetPaths];
8193
8690
  }
8194
8691
  function readHostedDistSnapshot(distPath) {
8195
- const resolvedDistPath = distPath ? resolveUserPath(distPath) : join31(process.cwd(), "dist");
8196
- const indexPath = join31(resolvedDistPath, "index.html");
8197
- if (!existsSync29(indexPath)) {
8692
+ const resolvedDistPath = distPath ? resolveUserPath(distPath) : join32(process.cwd(), "dist");
8693
+ const indexPath = join32(resolvedDistPath, "index.html");
8694
+ if (!existsSync30(indexPath)) {
8198
8695
  return void 0;
8199
8696
  }
8200
- const indexHtml = readFileSync22(indexPath, "utf-8");
8697
+ const indexHtml = readFileSync23(indexPath, "utf-8");
8201
8698
  const assetPaths = extractHostedAssetPaths(indexHtml);
8202
8699
  const assets = {};
8203
8700
  for (const assetPath of assetPaths) {
8204
- const assetFilePath = join31(resolvedDistPath, assetPath.replace(/^[/\\]+/, ""));
8205
- if (existsSync29(assetFilePath)) {
8206
- assets[assetPath] = readFileSync22(assetFilePath, "utf-8");
8701
+ const assetFilePath = join32(resolvedDistPath, assetPath.replace(/^[/\\]+/, ""));
8702
+ if (existsSync30(assetFilePath)) {
8703
+ assets[assetPath] = readFileSync23(assetFilePath, "utf-8");
8207
8704
  }
8208
8705
  }
8209
8706
  return {
@@ -8218,7 +8715,7 @@ function isHostedSourceSnapshotFile(path) {
8218
8715
  function readHostedSourceSnapshot(sourcePath) {
8219
8716
  if (!sourcePath) return void 0;
8220
8717
  const resolvedSourcePath = resolveUserPath(sourcePath);
8221
- if (!existsSync29(resolvedSourcePath)) {
8718
+ if (!existsSync30(resolvedSourcePath)) {
8222
8719
  return void 0;
8223
8720
  }
8224
8721
  const files = {};
@@ -8234,15 +8731,15 @@ function readHostedSourceSnapshot(sourcePath) {
8234
8731
  const walk = (absoluteDir, relativeDir) => {
8235
8732
  for (const entry of readdirSync9(absoluteDir, { withFileTypes: true })) {
8236
8733
  if (ignoredDirNames.has(entry.name)) continue;
8237
- const absolutePath = join31(absoluteDir, entry.name);
8238
- const relativePath = join31(relativeDir, entry.name).replace(/\\/g, "/");
8734
+ const absolutePath = join32(absoluteDir, entry.name);
8735
+ const relativePath = join32(relativeDir, entry.name).replace(/\\/g, "/");
8239
8736
  if (entry.isDirectory()) {
8240
8737
  walk(absolutePath, relativePath);
8241
8738
  continue;
8242
8739
  }
8243
8740
  if (!entry.isFile()) continue;
8244
8741
  if (!isHostedSourceSnapshotFile(relativePath)) continue;
8245
- files[relativePath] = readFileSync22(absolutePath, "utf-8");
8742
+ files[relativePath] = readFileSync23(absolutePath, "utf-8");
8246
8743
  }
8247
8744
  };
8248
8745
  walk(resolvedSourcePath, rootPrefix);
@@ -8435,24 +8932,24 @@ async function printHostedExecutionPackBundle(essencePath, namespace, jsonOutput
8435
8932
  }
8436
8933
  async function compileHostedExecutionPackBundle(essencePath, namespace) {
8437
8934
  const client = getPublicAPIClient();
8438
- const resolvedPath = essencePath ? resolveUserPath(essencePath) : join31(process.cwd(), "decantr.essence.json");
8439
- if (!existsSync29(resolvedPath)) {
8935
+ const resolvedPath = essencePath ? resolveUserPath(essencePath) : join32(process.cwd(), "decantr.essence.json");
8936
+ if (!existsSync30(resolvedPath)) {
8440
8937
  throw new Error(`Essence file not found at ${resolvedPath}`);
8441
8938
  }
8442
- const essence = JSON.parse(readFileSync22(resolvedPath, "utf-8"));
8939
+ const essence = JSON.parse(readFileSync23(resolvedPath, "utf-8"));
8443
8940
  const bundle = await client.compileExecutionPacks(essence, namespace ? { namespace } : void 0);
8444
- const contextDir = join31(dirname6(resolvedPath), ".decantr", "context");
8941
+ const contextDir = join32(dirname6(resolvedPath), ".decantr", "context");
8445
8942
  return { resolvedPath, bundle, contextDir };
8446
8943
  }
8447
8944
  function writeHostedExecutionPackContextArtifacts(contextDir, bundle) {
8448
- mkdirSync16(contextDir, { recursive: true });
8945
+ mkdirSync17(contextDir, { recursive: true });
8449
8946
  return writeExecutionPackBundleArtifacts(contextDir, bundle);
8450
8947
  }
8451
8948
  function resolvePagePackIdForRoute(essencePath, route) {
8452
- if (!existsSync29(essencePath)) {
8949
+ if (!existsSync30(essencePath)) {
8453
8950
  throw new Error(`Essence file not found at ${essencePath}`);
8454
8951
  }
8455
- const essence = JSON.parse(readFileSync22(essencePath, "utf-8"));
8952
+ const essence = JSON.parse(readFileSync23(essencePath, "utf-8"));
8456
8953
  if (!isV49(essence)) {
8457
8954
  throw new Error("Route-based pack resolution requires Essence v4.0.0.");
8458
8955
  }
@@ -8467,14 +8964,14 @@ function resolvePagePackIdForRoute(essencePath, route) {
8467
8964
  }
8468
8965
  async function printHostedSelectedExecutionPack(packType, id, essencePath, namespace, jsonOutput = false, writeContext = false) {
8469
8966
  const client = getPublicAPIClient();
8470
- const resolvedPath = essencePath ? resolveUserPath(essencePath) : join31(process.cwd(), "decantr.essence.json");
8471
- if (!existsSync29(resolvedPath)) {
8967
+ const resolvedPath = essencePath ? resolveUserPath(essencePath) : join32(process.cwd(), "decantr.essence.json");
8968
+ if (!existsSync30(resolvedPath)) {
8472
8969
  throw new Error(`Essence file not found at ${resolvedPath}`);
8473
8970
  }
8474
8971
  if ((packType === "section" || packType === "page" || packType === "mutation") && !id) {
8475
8972
  throw new Error(`Pack type "${packType}" requires an id.`);
8476
8973
  }
8477
- const essence = JSON.parse(readFileSync22(resolvedPath, "utf-8"));
8974
+ const essence = JSON.parse(readFileSync23(resolvedPath, "utf-8"));
8478
8975
  const selected = await client.selectExecutionPack(
8479
8976
  {
8480
8977
  essence,
@@ -8485,17 +8982,17 @@ async function printHostedSelectedExecutionPack(packType, id, essencePath, names
8485
8982
  );
8486
8983
  let writtenContextDir = null;
8487
8984
  if (writeContext) {
8488
- const contextDir = join31(dirname6(resolvedPath), ".decantr", "context");
8489
- mkdirSync16(contextDir, { recursive: true });
8490
- writeFileSync19(
8491
- join31(contextDir, "pack-manifest.json"),
8985
+ const contextDir = join32(dirname6(resolvedPath), ".decantr", "context");
8986
+ mkdirSync17(contextDir, { recursive: true });
8987
+ writeFileSync20(
8988
+ join32(contextDir, "pack-manifest.json"),
8492
8989
  JSON.stringify(selected.manifest, null, 2) + "\n"
8493
8990
  );
8494
8991
  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);
8495
8992
  const markdownFile = manifestEntry?.markdown ?? `${selected.selector.packType}${selected.selector.id ? `-${selected.selector.id}` : ""}-pack.md`;
8496
8993
  const jsonFile = manifestEntry?.json ?? `${selected.selector.packType}${selected.selector.id ? `-${selected.selector.id}` : ""}-pack.json`;
8497
- writeFileSync19(join31(contextDir, markdownFile), selected.pack.renderedMarkdown);
8498
- writeFileSync19(join31(contextDir, jsonFile), JSON.stringify(selected.pack, null, 2) + "\n");
8994
+ writeFileSync20(join32(contextDir, markdownFile), selected.pack.renderedMarkdown);
8995
+ writeFileSync20(join32(contextDir, jsonFile), JSON.stringify(selected.pack, null, 2) + "\n");
8499
8996
  writtenContextDir = contextDir;
8500
8997
  }
8501
8998
  if (jsonOutput) {
@@ -8520,20 +9017,20 @@ async function printHostedSelectedExecutionPack(packType, id, essencePath, names
8520
9017
  }
8521
9018
  async function printHostedExecutionPackManifest(essencePath, namespace, jsonOutput = false, writeContext = false) {
8522
9019
  const client = getPublicAPIClient();
8523
- const resolvedPath = essencePath ? resolveUserPath(essencePath) : join31(process.cwd(), "decantr.essence.json");
8524
- if (!existsSync29(resolvedPath)) {
9020
+ const resolvedPath = essencePath ? resolveUserPath(essencePath) : join32(process.cwd(), "decantr.essence.json");
9021
+ if (!existsSync30(resolvedPath)) {
8525
9022
  throw new Error(`Essence file not found at ${resolvedPath}`);
8526
9023
  }
8527
- const essence = JSON.parse(readFileSync22(resolvedPath, "utf-8"));
9024
+ const essence = JSON.parse(readFileSync23(resolvedPath, "utf-8"));
8528
9025
  const manifest = await client.getExecutionPackManifest(
8529
9026
  essence,
8530
9027
  namespace ? { namespace } : void 0
8531
9028
  );
8532
9029
  let writtenContextDir = null;
8533
9030
  if (writeContext) {
8534
- const contextDir = join31(dirname6(resolvedPath), ".decantr", "context");
8535
- mkdirSync16(contextDir, { recursive: true });
8536
- writeFileSync19(join31(contextDir, "pack-manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
9031
+ const contextDir = join32(dirname6(resolvedPath), ".decantr", "context");
9032
+ mkdirSync17(contextDir, { recursive: true });
9033
+ writeFileSync20(join32(contextDir, "pack-manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
8537
9034
  writtenContextDir = contextDir;
8538
9035
  }
8539
9036
  if (jsonOutput) {
@@ -8554,14 +9051,14 @@ async function printHostedExecutionPackManifest(essencePath, namespace, jsonOutp
8554
9051
  }
8555
9052
  }
8556
9053
  async function hydrateHostedExecutionPacksIfMissing(projectRoot, namespace = "@official") {
8557
- const contextDir = join31(projectRoot, ".decantr", "context");
8558
- const reviewPackPath = join31(contextDir, "review-pack.json");
8559
- const manifestPath = join31(contextDir, "pack-manifest.json");
8560
- if (existsSync29(reviewPackPath) && existsSync29(manifestPath)) {
9054
+ const contextDir = join32(projectRoot, ".decantr", "context");
9055
+ const reviewPackPath = join32(contextDir, "review-pack.json");
9056
+ const manifestPath = join32(contextDir, "pack-manifest.json");
9057
+ if (existsSync30(reviewPackPath) && existsSync30(manifestPath)) {
8561
9058
  return { attempted: false, hydrated: false };
8562
9059
  }
8563
- const essencePath = join31(projectRoot, "decantr.essence.json");
8564
- if (!existsSync29(essencePath)) {
9060
+ const essencePath = join32(projectRoot, "decantr.essence.json");
9061
+ if (!existsSync30(essencePath)) {
8565
9062
  return { attempted: false, hydrated: false };
8566
9063
  }
8567
9064
  const reviewHydration = await hydrateHostedReviewPackIfMissing(projectRoot, namespace);
@@ -8570,9 +9067,9 @@ async function hydrateHostedExecutionPacksIfMissing(projectRoot, namespace = "@o
8570
9067
  }
8571
9068
  try {
8572
9069
  const client = getPublicAPIClient();
8573
- const essence = JSON.parse(readFileSync22(essencePath, "utf-8"));
9070
+ const essence = JSON.parse(readFileSync23(essencePath, "utf-8"));
8574
9071
  const bundle = await client.compileExecutionPacks(essence, { namespace });
8575
- mkdirSync16(contextDir, { recursive: true });
9072
+ mkdirSync17(contextDir, { recursive: true });
8576
9073
  writeExecutionPackBundleArtifacts(contextDir, bundle);
8577
9074
  return { attempted: true, hydrated: true, scope: "bundle" };
8578
9075
  } catch {
@@ -8580,19 +9077,19 @@ async function hydrateHostedExecutionPacksIfMissing(projectRoot, namespace = "@o
8580
9077
  }
8581
9078
  }
8582
9079
  async function hydrateHostedReviewPackIfMissing(projectRoot, namespace = "@official") {
8583
- const contextDir = join31(projectRoot, ".decantr", "context");
8584
- const reviewPackPath = join31(contextDir, "review-pack.json");
8585
- const manifestPath = join31(contextDir, "pack-manifest.json");
8586
- if (existsSync29(reviewPackPath) && existsSync29(manifestPath)) {
9080
+ const contextDir = join32(projectRoot, ".decantr", "context");
9081
+ const reviewPackPath = join32(contextDir, "review-pack.json");
9082
+ const manifestPath = join32(contextDir, "pack-manifest.json");
9083
+ if (existsSync30(reviewPackPath) && existsSync30(manifestPath)) {
8587
9084
  return { attempted: false, hydrated: false };
8588
9085
  }
8589
- const essencePath = join31(projectRoot, "decantr.essence.json");
8590
- if (!existsSync29(essencePath)) {
9086
+ const essencePath = join32(projectRoot, "decantr.essence.json");
9087
+ if (!existsSync30(essencePath)) {
8591
9088
  return { attempted: false, hydrated: false };
8592
9089
  }
8593
9090
  try {
8594
9091
  const client = getPublicAPIClient();
8595
- const essence = JSON.parse(readFileSync22(essencePath, "utf-8"));
9092
+ const essence = JSON.parse(readFileSync23(essencePath, "utf-8"));
8596
9093
  const selected = await client.selectExecutionPack(
8597
9094
  {
8598
9095
  essence,
@@ -8600,14 +9097,14 @@ async function hydrateHostedReviewPackIfMissing(projectRoot, namespace = "@offic
8600
9097
  },
8601
9098
  { namespace }
8602
9099
  );
8603
- mkdirSync16(contextDir, { recursive: true });
8604
- writeFileSync19(join31(contextDir, "review-pack.md"), selected.pack.renderedMarkdown);
8605
- writeFileSync19(
8606
- join31(contextDir, "review-pack.json"),
9100
+ mkdirSync17(contextDir, { recursive: true });
9101
+ writeFileSync20(join32(contextDir, "review-pack.md"), selected.pack.renderedMarkdown);
9102
+ writeFileSync20(
9103
+ join32(contextDir, "review-pack.json"),
8607
9104
  JSON.stringify(selected.pack, null, 2) + "\n"
8608
9105
  );
8609
- if (!existsSync29(manifestPath)) {
8610
- writeFileSync19(manifestPath, JSON.stringify(selected.manifest, null, 2) + "\n");
9106
+ if (!existsSync30(manifestPath)) {
9107
+ writeFileSync20(manifestPath, JSON.stringify(selected.manifest, null, 2) + "\n");
8611
9108
  }
8612
9109
  return { attempted: true, hydrated: true, scope: "review" };
8613
9110
  } catch {
@@ -8617,17 +9114,17 @@ async function hydrateHostedReviewPackIfMissing(projectRoot, namespace = "@offic
8617
9114
  async function printHostedFileCritique(sourcePath, namespace, jsonOutput = false, essencePath, treatmentsPath) {
8618
9115
  const client = getPublicAPIClient();
8619
9116
  const resolvedSourcePath = resolveUserPath(sourcePath);
8620
- const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) : join31(process.cwd(), "decantr.essence.json");
8621
- const resolvedTreatmentsPath = treatmentsPath ? resolveUserPath(treatmentsPath) : join31(process.cwd(), "src", "styles", "treatments.css");
8622
- if (!existsSync29(resolvedSourcePath)) {
9117
+ const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) : join32(process.cwd(), "decantr.essence.json");
9118
+ const resolvedTreatmentsPath = treatmentsPath ? resolveUserPath(treatmentsPath) : join32(process.cwd(), "src", "styles", "treatments.css");
9119
+ if (!existsSync30(resolvedSourcePath)) {
8623
9120
  throw new Error(`Source file not found at ${resolvedSourcePath}`);
8624
9121
  }
8625
- if (!existsSync29(resolvedEssencePath)) {
9122
+ if (!existsSync30(resolvedEssencePath)) {
8626
9123
  throw new Error(`Essence file not found at ${resolvedEssencePath}`);
8627
9124
  }
8628
- const code = readFileSync22(resolvedSourcePath, "utf-8");
8629
- const essence = JSON.parse(readFileSync22(resolvedEssencePath, "utf-8"));
8630
- const treatmentsCss = existsSync29(resolvedTreatmentsPath) ? readFileSync22(resolvedTreatmentsPath, "utf-8") : void 0;
9125
+ const code = readFileSync23(resolvedSourcePath, "utf-8");
9126
+ const essence = JSON.parse(readFileSync23(resolvedEssencePath, "utf-8"));
9127
+ const treatmentsCss = existsSync30(resolvedTreatmentsPath) ? readFileSync23(resolvedTreatmentsPath, "utf-8") : void 0;
8631
9128
  const report = await client.critiqueFile(
8632
9129
  {
8633
9130
  essence,
@@ -8651,11 +9148,11 @@ async function printHostedFileCritique(sourcePath, namespace, jsonOutput = false
8651
9148
  }
8652
9149
  async function printHostedProjectAudit(namespace, jsonOutput = false, essencePath, distPath, sourcesPath) {
8653
9150
  const client = getPublicAPIClient();
8654
- const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) : join31(process.cwd(), "decantr.essence.json");
8655
- if (!existsSync29(resolvedEssencePath)) {
9151
+ const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) : join32(process.cwd(), "decantr.essence.json");
9152
+ if (!existsSync30(resolvedEssencePath)) {
8656
9153
  throw new Error(`Essence file not found at ${resolvedEssencePath}`);
8657
9154
  }
8658
- const essence = JSON.parse(readFileSync22(resolvedEssencePath, "utf-8"));
9155
+ const essence = JSON.parse(readFileSync23(resolvedEssencePath, "utf-8"));
8659
9156
  const dist = readHostedDistSnapshot(distPath);
8660
9157
  const sources = readHostedSourceSnapshot(sourcesPath);
8661
9158
  const report = await client.auditProject(
@@ -8673,7 +9170,7 @@ async function printHostedProjectAudit(namespace, jsonOutput = false, essencePat
8673
9170
  console.log(heading2("Hosted Project Audit"));
8674
9171
  console.log(` Essence: ${resolvedEssencePath}`);
8675
9172
  console.log(
8676
- ` Dist snapshot: ${dist ? distPath ? resolveUserPath(distPath) : join31(process.cwd(), "dist") : "none"}`
9173
+ ` Dist snapshot: ${dist ? distPath ? resolveUserPath(distPath) : join32(process.cwd(), "dist") : "none"}`
8677
9174
  );
8678
9175
  console.log(
8679
9176
  ` Source snapshot: ${sources && sourcesPath ? resolveUserPath(sourcesPath) : "none"}`
@@ -8776,21 +9273,21 @@ function patternCandidateFromRegistryItem(item, source) {
8776
9273
  function readSuggestCodeContext(projectRoot, route, file) {
8777
9274
  const pieces = [];
8778
9275
  if (file) {
8779
- const resolved = isAbsolute3(file) ? file : join31(projectRoot, file);
8780
- if (existsSync29(resolved)) {
8781
- pieces.push(readFileSync22(resolved, "utf-8"));
9276
+ const resolved = isAbsolute3(file) ? file : join32(projectRoot, file);
9277
+ if (existsSync30(resolved)) {
9278
+ pieces.push(readFileSync23(resolved, "utf-8"));
8782
9279
  }
8783
9280
  }
8784
9281
  if (route) {
8785
- const analysisPath = join31(projectRoot, ".decantr", "analysis.json");
8786
- if (existsSync29(analysisPath)) {
9282
+ const analysisPath = join32(projectRoot, ".decantr", "analysis.json");
9283
+ if (existsSync30(analysisPath)) {
8787
9284
  try {
8788
- const analysis = JSON.parse(readFileSync22(analysisPath, "utf-8"));
9285
+ const analysis = JSON.parse(readFileSync23(analysisPath, "utf-8"));
8789
9286
  const routeEntry = analysis.routes?.routes?.find((entry) => entry.path === route);
8790
9287
  if (routeEntry?.file) {
8791
- const resolved = join31(projectRoot, routeEntry.file);
8792
- if (existsSync29(resolved)) {
8793
- pieces.push(readFileSync22(resolved, "utf-8"));
9288
+ const resolved = join32(projectRoot, routeEntry.file);
9289
+ if (existsSync30(resolved)) {
9290
+ pieces.push(readFileSync23(resolved, "utf-8"));
8794
9291
  }
8795
9292
  }
8796
9293
  } catch {
@@ -8803,7 +9300,7 @@ function localPatternMatches(projectRoot, query) {
8803
9300
  if (!projectRoot) return [];
8804
9301
  const pack = readLocalPatternPack(projectRoot);
8805
9302
  const patterns = Array.isArray(pack?.patterns) ? pack.patterns : [];
8806
- const queryTerms = query.toLowerCase().split(/[^a-z0-9]+/).filter((term) => term.length > 1);
9303
+ 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]);
8807
9304
  if (queryTerms.length === 0) return [];
8808
9305
  return patterns.map((pattern) => {
8809
9306
  const id = typeof pattern.id === "string" ? pattern.id : "local-pattern";
@@ -8815,7 +9312,11 @@ function localPatternMatches(projectRoot, query) {
8815
9312
  role,
8816
9313
  typeof pattern.decide === "string" ? pattern.decide : null,
8817
9314
  ...Array.isArray(pattern.appliesTo) ? pattern.appliesTo : [],
8818
- ...Array.isArray(pattern.componentPaths) ? pattern.componentPaths : []
9315
+ ...Array.isArray(pattern.componentPaths) ? pattern.componentPaths : [],
9316
+ ...Array.isArray(pattern.tokenHints) ? pattern.tokenHints : [],
9317
+ ...Array.isArray(pattern.classHints) ? pattern.classHints : [],
9318
+ ...Array.isArray(pattern.evidence) ? pattern.evidence : [],
9319
+ ...Array.isArray(pattern.forbiddenAlternatives) ? pattern.forbiddenAlternatives : []
8819
9320
  ].filter((entry) => typeof entry === "string").join(" ").toLowerCase();
8820
9321
  const score = queryTerms.reduce((sum, term) => sum + (haystack.includes(term) ? 1 : 0), 0);
8821
9322
  return { id, label, role, score };
@@ -8849,6 +9350,7 @@ async function loadPatternDiscoveryCandidates(registryClient) {
8849
9350
  }
8850
9351
  async function cmdSuggest(query, options = {}) {
8851
9352
  const searchType = options.type || "pattern";
9353
+ const projectRoot = options.projectRoot ?? process.cwd();
8852
9354
  if (searchType !== "pattern" && searchType !== "patterns") {
8853
9355
  const apiClient = getAPIClient();
8854
9356
  try {
@@ -8869,9 +9371,11 @@ async function cmdSuggest(query, options = {}) {
8869
9371
  }
8870
9372
  }
8871
9373
  const registryClient = new RegistryClient({
8872
- cacheDir: join31(options.projectRoot ?? process.cwd(), ".decantr", "cache")
9374
+ cacheDir: join32(projectRoot, ".decantr", "cache")
8873
9375
  });
8874
- const code = options.fromCode || options.file ? readSuggestCodeContext(options.projectRoot ?? process.cwd(), options.route, options.file) : "";
9376
+ const code = options.fromCode || options.file ? readSuggestCodeContext(projectRoot, options.route, options.file) : "";
9377
+ const localMatches = localPatternMatches(projectRoot, [query, code].filter(Boolean).join("\n"));
9378
+ const bridgeMatches = styleBridgeMatches(projectRoot, [query, code].filter(Boolean).join("\n"));
8875
9379
  const candidates = await loadPatternDiscoveryCandidates(registryClient);
8876
9380
  const matches = rankPatternCandidates(
8877
9381
  {
@@ -8882,7 +9386,7 @@ async function cmdSuggest(query, options = {}) {
8882
9386
  },
8883
9387
  candidates
8884
9388
  );
8885
- if (matches.length === 0) {
9389
+ if (matches.length === 0 && localMatches.length === 0 && bridgeMatches.length === 0) {
8886
9390
  console.log(dim3(`No pattern suggestions for "${query}"`));
8887
9391
  console.log("");
8888
9392
  console.log("Try:");
@@ -8902,10 +9406,14 @@ async function cmdSuggest(query, options = {}) {
8902
9406
  `Pattern suggestions for "${query}"${contextBits.length > 0 ? ` (${contextBits.join(", ")})` : ""}`
8903
9407
  )
8904
9408
  );
8905
- const localMatches = localPatternMatches(
8906
- options.projectRoot,
8907
- [query, code].filter(Boolean).join("\n")
8908
- );
9409
+ if (bridgeMatches.length > 0) {
9410
+ console.log(`${BOLD9}Project-owned style bridge:${RESET16}`);
9411
+ for (const match of bridgeMatches) {
9412
+ const hints = [...match.tokenHints, ...match.classHints].slice(0, 3).join(", ");
9413
+ console.log(` ${cyan3(match.id)} ${match.label}${hints ? ` ${dim3(hints)}` : ""}`);
9414
+ }
9415
+ console.log("");
9416
+ }
8909
9417
  if (localMatches.length > 0) {
8910
9418
  console.log(`${BOLD9}Project-owned local law:${RESET16}`);
8911
9419
  for (const match of localMatches) {
@@ -8915,6 +9423,12 @@ async function cmdSuggest(query, options = {}) {
8915
9423
  console.log("");
8916
9424
  console.log(`${BOLD9}Registry patterns:${RESET16}`);
8917
9425
  }
9426
+ if (matches.length === 0) {
9427
+ console.log(dim3("No hosted/bundled registry patterns matched this query."));
9428
+ console.log("");
9429
+ console.log(dim3('Use local law first, or run "decantr list patterns" to browse registry options.'));
9430
+ return;
9431
+ }
8918
9432
  for (const match of matches.slice(0, 8)) {
8919
9433
  const candidate = match.candidate;
8920
9434
  const slug = candidate.slug || candidate.id;
@@ -8944,7 +9458,7 @@ async function cmdGet(type, id) {
8944
9458
  }
8945
9459
  const apiType = CONTENT_TYPE_TO_API_CONTENT_TYPE3[type];
8946
9460
  const registryClient = new RegistryClient({
8947
- cacheDir: join31(process.cwd(), ".decantr", "cache")
9461
+ cacheDir: join32(process.cwd(), ".decantr", "cache")
8948
9462
  });
8949
9463
  const result = await registryClient.fetchContentItem(apiType, id);
8950
9464
  if (result) {
@@ -8961,10 +9475,10 @@ async function cmdGet(type, id) {
8961
9475
  return;
8962
9476
  }
8963
9477
  async function cmdValidate(path) {
8964
- const essencePath = path || join31(process.cwd(), "decantr.essence.json");
9478
+ const essencePath = path || join32(process.cwd(), "decantr.essence.json");
8965
9479
  let raw;
8966
9480
  try {
8967
- raw = readFileSync22(essencePath, "utf-8");
9481
+ raw = readFileSync23(essencePath, "utf-8");
8968
9482
  } catch {
8969
9483
  console.error(error2(`Could not read ${essencePath}`));
8970
9484
  process.exitCode = 1;
@@ -9031,7 +9545,7 @@ async function cmdList(type, sort, recommended, intelligenceSource, blueprintSet
9031
9545
  return;
9032
9546
  }
9033
9547
  const registryClient = new RegistryClient({
9034
- cacheDir: join31(process.cwd(), ".decantr", "cache")
9548
+ cacheDir: join32(process.cwd(), ".decantr", "cache")
9035
9549
  });
9036
9550
  const result = await registryClient.fetchContentList(
9037
9551
  type,
@@ -9114,10 +9628,10 @@ ${CYAN9}Telemetry enabled.${RESET16} Decantr will send privacy-filtered CLI prod
9114
9628
  }
9115
9629
  function readCliPackageVersion2() {
9116
9630
  const here = dirname6(fileURLToPath3(import.meta.url));
9117
- const candidates = [join31(here, "..", "package.json"), join31(here, "..", "..", "package.json")];
9631
+ const candidates = [join32(here, "..", "package.json"), join32(here, "..", "..", "package.json")];
9118
9632
  for (const candidate of candidates) {
9119
9633
  try {
9120
- const pkg = JSON.parse(readFileSync22(candidate, "utf-8"));
9634
+ const pkg = JSON.parse(readFileSync23(candidate, "utf-8"));
9121
9635
  if (pkg.version) return pkg.version;
9122
9636
  } catch {
9123
9637
  }
@@ -9128,19 +9642,19 @@ function timestampForFile() {
9128
9642
  return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
9129
9643
  }
9130
9644
  function backupExistingEssence(projectRoot, label) {
9131
- const essencePath = join31(projectRoot, "decantr.essence.json");
9132
- if (!existsSync29(essencePath)) return null;
9133
- const backupPath = join31(
9645
+ const essencePath = join32(projectRoot, "decantr.essence.json");
9646
+ if (!existsSync30(essencePath)) return null;
9647
+ const backupPath = join32(
9134
9648
  projectRoot,
9135
9649
  `decantr.essence.${label}.${timestampForFile()}.backup.json`
9136
9650
  );
9137
- writeFileSync19(backupPath, readFileSync22(essencePath, "utf-8"), "utf-8");
9651
+ writeFileSync20(backupPath, readFileSync23(essencePath, "utf-8"), "utf-8");
9138
9652
  return backupPath;
9139
9653
  }
9140
9654
  function writeBrownfieldProjectJson(input) {
9141
- const decantrDir = join31(input.projectRoot, ".decantr");
9142
- mkdirSync16(join31(decantrDir, "context"), { recursive: true });
9143
- mkdirSync16(join31(decantrDir, "cache"), { recursive: true });
9655
+ const decantrDir = join32(input.projectRoot, ".decantr");
9656
+ mkdirSync17(join32(decantrDir, "context"), { recursive: true });
9657
+ mkdirSync17(join32(decantrDir, "cache"), { recursive: true });
9144
9658
  const now = (/* @__PURE__ */ new Date()).toISOString();
9145
9659
  const projectJson = {
9146
9660
  detected: {
@@ -9181,7 +9695,7 @@ function writeBrownfieldProjectJson(input) {
9181
9695
  }
9182
9696
  }
9183
9697
  };
9184
- writeFileSync19(join31(decantrDir, "project.json"), JSON.stringify(projectJson, null, 2) + "\n");
9698
+ writeFileSync20(join32(decantrDir, "project.json"), JSON.stringify(projectJson, null, 2) + "\n");
9185
9699
  }
9186
9700
  async function applyAcceptedBrownfieldProposal(input) {
9187
9701
  const proposal = readBrownfieldProposal(input.projectRoot);
@@ -9197,8 +9711,8 @@ async function applyAcceptedBrownfieldProposal(input) {
9197
9711
  process.exitCode = 1;
9198
9712
  return;
9199
9713
  }
9200
- const essencePath = join31(input.projectRoot, "decantr.essence.json");
9201
- const hasEssence = existsSync29(essencePath);
9714
+ const essencePath = join32(input.projectRoot, "decantr.essence.json");
9715
+ const hasEssence = existsSync30(essencePath);
9202
9716
  let essence;
9203
9717
  let backupPath = null;
9204
9718
  if (input.mode === "accept" && hasEssence) {
@@ -9212,7 +9726,7 @@ async function applyAcceptedBrownfieldProposal(input) {
9212
9726
  return;
9213
9727
  }
9214
9728
  if (input.mode === "merge" && hasEssence) {
9215
- const existing = JSON.parse(readFileSync22(essencePath, "utf-8"));
9729
+ const existing = JSON.parse(readFileSync23(essencePath, "utf-8"));
9216
9730
  if (!isV49(existing)) {
9217
9731
  console.log(
9218
9732
  error2(
@@ -9249,9 +9763,9 @@ async function applyAcceptedBrownfieldProposal(input) {
9249
9763
  assistantBridge: input.assistantBridge,
9250
9764
  mode: input.mode
9251
9765
  });
9252
- writeFileSync19(essencePath, JSON.stringify(essence, null, 2) + "\n", "utf-8");
9766
+ writeFileSync20(essencePath, JSON.stringify(essence, null, 2) + "\n", "utf-8");
9253
9767
  const registryClient = new RegistryClient({
9254
- cacheDir: join31(input.projectRoot, ".decantr", "cache"),
9768
+ cacheDir: join32(input.projectRoot, ".decantr", "cache"),
9255
9769
  offline: true,
9256
9770
  projectRoot: input.projectRoot
9257
9771
  });
@@ -9313,18 +9827,23 @@ async function applyAcceptedBrownfieldProposal(input) {
9313
9827
  if (refreshResult.contextFiles.length > 8) {
9314
9828
  console.log(` ${dim3(`(+${refreshResult.contextFiles.length - 8} more)`)}`);
9315
9829
  }
9316
- console.log("");
9317
- console.log(" Next steps:");
9318
- console.log(
9319
- ` 1. Run ${cyan3(withProject("decantr check --brownfield", projectLabel))} to verify contract coverage`
9320
- );
9321
- console.log(
9322
- ` 2. Read ${cyan3(displayProjectPath(input.workspaceInfo, ".decantr/brownfield-report.md"))} for unresolved doctrine risks`
9323
- );
9324
- console.log(
9325
- ` 3. Use ${cyan3(withProject("decantr rules preview", projectLabel))} before mutating assistant rule files`
9326
- );
9327
- console.log("");
9830
+ if (!input.suppressNextSteps) {
9831
+ console.log("");
9832
+ console.log(" Next steps:");
9833
+ console.log(
9834
+ ` 1. Run ${cyan3(withProject("decantr doctor", projectLabel))} to explain adoption state and the next command`
9835
+ );
9836
+ console.log(
9837
+ ` 2. Run ${cyan3(withProject("decantr codify --from-audit", projectLabel))} when you are ready to propose project-owned UI law`
9838
+ );
9839
+ console.log(
9840
+ ` 3. Use ${cyan3(withProject('decantr task / "change summary"', projectLabel))} before LLM edits`
9841
+ );
9842
+ console.log(
9843
+ ` 4. Run ${cyan3(withProject("decantr verify --brownfield", projectLabel))} after edits`
9844
+ );
9845
+ console.log("");
9846
+ }
9328
9847
  }
9329
9848
  async function cmdInit(args) {
9330
9849
  const workspaceInfo = resolveWorkspaceInfo(process.cwd(), args.project);
@@ -9372,7 +9891,8 @@ async function cmdInit(args) {
9372
9891
  detected,
9373
9892
  workspaceInfo,
9374
9893
  mode: proposalMode,
9375
- assistantBridge: policy.assistantBridge
9894
+ assistantBridge: policy.assistantBridge,
9895
+ suppressNextSteps: args.internalSuppressNextSteps
9376
9896
  });
9377
9897
  if (args.telemetry) enableCliTelemetry(projectRoot);
9378
9898
  writeArtifactReadme(projectRoot);
@@ -9424,7 +9944,7 @@ async function cmdInit(args) {
9424
9944
  }
9425
9945
  }
9426
9946
  const registryClient = new RegistryClient({
9427
- cacheDir: join31(projectRoot, ".decantr", "cache"),
9947
+ cacheDir: join32(projectRoot, ".decantr", "cache"),
9428
9948
  apiUrl: args.registry,
9429
9949
  offline: args.offline,
9430
9950
  projectRoot
@@ -9756,7 +10276,7 @@ ${YELLOW12}You're offline. Scaffolding Decantr default.${RESET16}`);
9756
10276
  if (appliedRuleFiles.length > 0) {
9757
10277
  console.log(` ${dim3(`Rule bridge applied: ${appliedRuleFiles.join(", ")}`)}`);
9758
10278
  }
9759
- if (!existsSync29(join31(projectRoot, "package.json"))) {
10279
+ if (!existsSync30(join32(projectRoot, "package.json"))) {
9760
10280
  console.log("");
9761
10281
  console.log(
9762
10282
  dim3(` Note: ${cyan3("decantr init")} created Decantr contract/context files only.`)
@@ -9767,7 +10287,7 @@ ${YELLOW12}You're offline. Scaffolding Decantr default.${RESET16}`);
9767
10287
  )
9768
10288
  );
9769
10289
  }
9770
- const hasCompiledPacks = existsSync29(join31(projectRoot, ".decantr", "context", "scaffold-pack.md"));
10290
+ const hasCompiledPacks = existsSync30(join32(projectRoot, ".decantr", "context", "scaffold-pack.md"));
9771
10291
  console.log("");
9772
10292
  console.log(" Next steps:");
9773
10293
  if (hasCompiledPacks) {
@@ -9807,7 +10327,7 @@ ${YELLOW12}You're offline. Scaffolding Decantr default.${RESET16}`);
9807
10327
  console.log(` ${cyan3("decantr upgrade")} Update to latest patterns`);
9808
10328
  console.log(` ${cyan3("decantr check")} Detect drift issues`);
9809
10329
  console.log(` ${cyan3("decantr migrate --to v4")} Migrate older essence files to v4`);
9810
- const essenceContent = readFileSync22(result.essencePath, "utf-8");
10330
+ const essenceContent = readFileSync23(result.essencePath, "utf-8");
9811
10331
  const essence = JSON.parse(essenceContent);
9812
10332
  const validation = validateEssence2(essence);
9813
10333
  if (!validation.valid) {
@@ -9862,16 +10382,16 @@ Validation warnings: ${validation.errors.join(", ")}`));
9862
10382
  }
9863
10383
  }
9864
10384
  async function cmdStatus(projectRoot = process.cwd()) {
9865
- const essencePath = join31(projectRoot, "decantr.essence.json");
9866
- const projectJsonPath = join31(projectRoot, ".decantr", "project.json");
10385
+ const essencePath = join32(projectRoot, "decantr.essence.json");
10386
+ const projectJsonPath = join32(projectRoot, ".decantr", "project.json");
9867
10387
  console.log(heading2("Decantr Project Status"));
9868
- if (!existsSync29(essencePath)) {
10388
+ if (!existsSync30(essencePath)) {
9869
10389
  console.log(`${RED12}No decantr.essence.json found.${RESET16}`);
9870
10390
  console.log(dim3('Run "decantr init" to create one.'));
9871
10391
  return;
9872
10392
  }
9873
10393
  try {
9874
- const essence = JSON.parse(readFileSync22(essencePath, "utf-8"));
10394
+ const essence = JSON.parse(readFileSync23(essencePath, "utf-8"));
9875
10395
  const validation = validateEssence2(essence);
9876
10396
  const essenceVersion = isV49(essence) ? "v4" : "legacy";
9877
10397
  console.log(`${BOLD9}Essence:${RESET16}`);
@@ -9919,9 +10439,9 @@ async function cmdStatus(projectRoot = process.cwd()) {
9919
10439
  }
9920
10440
  console.log("");
9921
10441
  console.log(`${BOLD9}Sync Status:${RESET16}`);
9922
- if (existsSync29(projectJsonPath)) {
10442
+ if (existsSync30(projectJsonPath)) {
9923
10443
  try {
9924
- const projectJson = JSON.parse(readFileSync22(projectJsonPath, "utf-8"));
10444
+ const projectJson = JSON.parse(readFileSync23(projectJsonPath, "utf-8"));
9925
10445
  const syncStatus = projectJson.sync?.status || "unknown";
9926
10446
  const lastSync = projectJson.sync?.lastSync || "never";
9927
10447
  const source = projectJson.sync?.registrySource || "unknown";
@@ -9939,7 +10459,7 @@ async function cmdStatus(projectRoot = process.cwd()) {
9939
10459
  }
9940
10460
  async function cmdSync() {
9941
10461
  const projectRoot = process.cwd();
9942
- const cacheDir = join31(projectRoot, ".decantr", "cache");
10462
+ const cacheDir = join32(projectRoot, ".decantr", "cache");
9943
10463
  console.log(heading2("Syncing registry content..."));
9944
10464
  const result = await syncRegistry(cacheDir);
9945
10465
  if (result.synced.length > 0) {
@@ -10133,14 +10653,14 @@ ${BOLD9}Examples:${RESET16}
10133
10653
  process.exitCode = 1;
10134
10654
  return;
10135
10655
  }
10136
- const themePath = join31(projectRoot, ".decantr", "custom", "themes", `${name}.json`);
10137
- if (!existsSync29(themePath)) {
10656
+ const themePath = join32(projectRoot, ".decantr", "custom", "themes", `${name}.json`);
10657
+ if (!existsSync30(themePath)) {
10138
10658
  console.error(error2(`Theme "${name}" not found at ${themePath}`));
10139
10659
  process.exitCode = 1;
10140
10660
  return;
10141
10661
  }
10142
10662
  try {
10143
- const theme = JSON.parse(readFileSync22(themePath, "utf-8"));
10663
+ const theme = JSON.parse(readFileSync23(themePath, "utf-8"));
10144
10664
  const result = validateCustomTheme(theme);
10145
10665
  if (result.valid) {
10146
10666
  console.log(success3(`Custom theme "${name}" is valid`));
@@ -10272,7 +10792,7 @@ function withProject(command, projectArg) {
10272
10792
  return projectArg ? `${command} --project ${projectArg}` : command;
10273
10793
  }
10274
10794
  function displayProjectPath(workspaceInfo, projectPath) {
10275
- const absolutePath = join31(workspaceInfo.appRoot, projectPath);
10795
+ const absolutePath = join32(workspaceInfo.appRoot, projectPath);
10276
10796
  const relativePath = relative7(workspaceInfo.cwd, absolutePath).replace(/\\/g, "/");
10277
10797
  if (relativePath && !relativePath.startsWith("..") && !isAbsolute3(relativePath)) {
10278
10798
  return relativePath;
@@ -10331,6 +10851,7 @@ function printWorkspaceProjectSelection(workspaceInfo, commandName = "command")
10331
10851
  console.log("");
10332
10852
  console.log("Start by attaching one app:");
10333
10853
  console.log(` ${cyan3(`decantr adopt --project ${candidate} --yes`)}`);
10854
+ console.log(` ${cyan3(`decantr codify --from-audit --style-bridge --project ${candidate}`)}`);
10334
10855
  console.log("");
10335
10856
  console.log("Optional visual evidence after the app is running:");
10336
10857
  console.log(
@@ -10340,7 +10861,7 @@ function printWorkspaceProjectSelection(workspaceInfo, commandName = "command")
10340
10861
  function printMonorepoSetupGuidance(workspaceInfo) {
10341
10862
  const candidate = firstWorkspaceCandidate(workspaceInfo);
10342
10863
  const attachedProjects = workspaceInfo.appCandidates.filter(
10343
- (appCandidate) => existsSync29(join31(workspaceInfo.workspaceRoot, appCandidate, "decantr.essence.json"))
10864
+ (appCandidate) => existsSync30(join32(workspaceInfo.workspaceRoot, appCandidate, "decantr.essence.json"))
10344
10865
  );
10345
10866
  const firstAttached = attachedProjects[0];
10346
10867
  console.log(heading2("Decantr Setup"));
@@ -10397,7 +10918,7 @@ function printMonorepoSetupGuidance(workspaceInfo) {
10397
10918
  ` ${cyan3(`decantr adopt --project ${candidate} --yes`)} Attach Decantr to one app`
10398
10919
  );
10399
10920
  console.log(
10400
- ` ${cyan3(`decantr codify --from-audit --project ${candidate}`)} Propose project-owned UI law`
10921
+ ` ${cyan3(`decantr codify --from-audit --style-bridge --project ${candidate}`)} Propose project-owned UI law and style bridge`
10401
10922
  );
10402
10923
  console.log("");
10403
10924
  console.log(`${BOLD9}Optional visual evidence:${RESET16}`);
@@ -10408,7 +10929,7 @@ function printMonorepoSetupGuidance(workspaceInfo) {
10408
10929
  function resolveWorkflowProject(flags, commandName = "command", options = {}) {
10409
10930
  const projectArg = flagString(flags, "project");
10410
10931
  const workspaceInfo = resolveWorkspaceInfo(process.cwd(), projectArg);
10411
- if (projectArg && options.requireExisting !== false && !existsSync29(workspaceInfo.appRoot)) {
10932
+ if (projectArg && options.requireExisting !== false && !existsSync30(workspaceInfo.appRoot)) {
10412
10933
  printProjectNotFound(projectArg, commandName);
10413
10934
  process.exitCode = 1;
10414
10935
  return null;
@@ -10468,21 +10989,27 @@ async function cmdSetupWorkflow(args) {
10468
10989
  console.log(` Detected: ${formatDetection(detected)}`);
10469
10990
  console.log("");
10470
10991
  if (detected.existingEssence) {
10471
- const hasLocalPatterns = existsSync29(localPatternsPath(workspaceInfo.appRoot));
10472
- const hasLocalRules = existsSync29(localRulesPath(workspaceInfo.appRoot));
10992
+ const hasLocalPatterns = existsSync30(localPatternsPath(workspaceInfo.appRoot));
10993
+ const hasLocalRules = existsSync30(localRulesPath(workspaceInfo.appRoot));
10994
+ const hasStyleBridge = existsSync30(styleBridgePath(workspaceInfo.appRoot));
10473
10995
  const verifyCommand = hasLocalPatterns || hasLocalRules ? "decantr verify --brownfield --local-patterns" : "decantr verify --brownfield";
10474
10996
  console.log(`${BOLD9}Recommended path:${RESET16} maintain an attached Decantr project`);
10997
+ console.log(
10998
+ ` ${cyan3(withProject("decantr doctor", projectArg))} Explain current state and next command`
10999
+ );
11000
+ if (!hasLocalPatterns || !hasLocalRules || !hasStyleBridge) {
11001
+ const codifyCommand = hasLocalPatterns && hasLocalRules ? "decantr codify --style-bridge" : "decantr codify --from-audit --style-bridge";
11002
+ console.log(
11003
+ ` ${cyan3(withProject(codifyCommand, projectArg))} Propose missing local law/style bridge`
11004
+ );
11005
+ }
10475
11006
  console.log(
10476
11007
  ` ${cyan3(withProject('decantr task <route> "<change>"', projectArg))} Prepare LLM context before edits`
10477
11008
  );
10478
11009
  console.log(
10479
11010
  ` ${cyan3(withProject(verifyCommand, projectArg))} Run local health and drift checks`
10480
11011
  );
10481
- if (!hasLocalPatterns || !hasLocalRules) {
10482
- console.log(
10483
- ` ${cyan3(withProject("decantr codify --from-audit", projectArg))} Propose project-owned local law`
10484
- );
10485
- }
11012
+ console.log(` ${cyan3(withProject("decantr ci init", projectArg))} Wire the app into CI`);
10486
11013
  return;
10487
11014
  }
10488
11015
  if (hasFootprint) {
@@ -10491,7 +11018,7 @@ async function cmdSetupWorkflow(args) {
10491
11018
  ` ${cyan3(withProject("decantr adopt --yes", projectArg))} Analyze, attach, and verify`
10492
11019
  );
10493
11020
  console.log(
10494
- ` ${cyan3(withProject("decantr codify --from-audit", projectArg))} Propose local UI law`
11021
+ ` ${cyan3(withProject("decantr codify --from-audit --style-bridge", projectArg))} Propose local UI law and style bridge`
10495
11022
  );
10496
11023
  console.log("");
10497
11024
  console.log(`${BOLD9}Optional visual evidence after the app is running:${RESET16}`);
@@ -10550,7 +11077,7 @@ async function cmdAdoptWorkflow(args) {
10550
11077
  const hydratePacks = flagBoolean(flags, "packs", true) && !flagBoolean(flags, "skip-packs") && !flagBoolean(flags, "offline") && process.env.DECANTR_OFFLINE !== "true";
10551
11078
  const initCi = flagBoolean(flags, "ci") || flagBoolean(flags, "init-ci");
10552
11079
  const assistantBridge = flagString(flags, "assistant-bridge");
10553
- const hasEssence = existsSync29(join31(projectRoot, "decantr.essence.json"));
11080
+ const hasEssence = existsSync30(join32(projectRoot, "decantr.essence.json"));
10554
11081
  const proposalFlag = flagBoolean(flags, "replace-essence") ? "--replace-essence" : flagBoolean(flags, "merge-proposal") || hasEssence ? "--merge-proposal" : "--accept-proposal";
10555
11082
  const steps = [
10556
11083
  "analyze current app and write .decantr/brownfield intelligence",
@@ -10591,13 +11118,14 @@ async function cmdAdoptWorkflow(args) {
10591
11118
  "merge-proposal": proposalFlag === "--merge-proposal",
10592
11119
  "replace-essence": proposalFlag === "--replace-essence",
10593
11120
  "assistant-bridge": assistantBridge,
10594
- telemetry: flagBoolean(flags, "telemetry")
11121
+ telemetry: flagBoolean(flags, "telemetry"),
11122
+ internalSuppressNextSteps: true
10595
11123
  });
10596
11124
  if (process.exitCode && process.exitCode !== 0) return;
10597
11125
  if (hydratePacks) {
10598
11126
  try {
10599
11127
  const { bundle, contextDir } = await compileHostedExecutionPackBundle(
10600
- join31(projectRoot, "decantr.essence.json")
11128
+ join32(projectRoot, "decantr.essence.json")
10601
11129
  );
10602
11130
  const written = writeHostedExecutionPackContextArtifacts(
10603
11131
  contextDir,
@@ -10620,7 +11148,7 @@ async function cmdAdoptWorkflow(args) {
10620
11148
  console.log(dim3("Skipping hosted pack hydration in offline mode."));
10621
11149
  }
10622
11150
  if (runVerify) {
10623
- const { cmdHealth } = await import("./health-VAYRQVTF.js");
11151
+ const { cmdHealth } = await import("./health-DBCUR6VQ.js");
10624
11152
  await cmdHealth(projectRoot, {
10625
11153
  browser: runBrowser,
10626
11154
  browserBaseUrl: baseUrl,
@@ -10638,10 +11166,10 @@ async function cmdAdoptWorkflow(args) {
10638
11166
  console.log("");
10639
11167
  console.log(`${BOLD9}Brownfield operating loop:${RESET16}`);
10640
11168
  console.log(
10641
- ` ${cyan3(withProject("decantr codify --from-audit", projectArg))} Discover and propose project-owned UI law`
11169
+ ` ${cyan3(withProject("decantr codify --from-audit --style-bridge", projectArg))} Propose project-owned UI law and style bridge`
10642
11170
  );
10643
11171
  console.log(
10644
- ` ${cyan3(withProject("decantr codify --accept", projectArg))} Accept reviewed local patterns and rules`
11172
+ ` ${cyan3(withProject("decantr codify --accept", projectArg))} Accept reviewed local patterns, rules, and bridge`
10645
11173
  );
10646
11174
  console.log(
10647
11175
  ` ${cyan3(withProject('decantr task <route> "<change>"', projectArg))} Give your LLM route-specific context before edits`
@@ -10691,7 +11219,7 @@ async function cmdVerifyWorkflow(args) {
10691
11219
  return;
10692
11220
  }
10693
11221
  if (workspaceMode) {
10694
- const { cmdWorkspace } = await import("./workspace-PBO3BJRL.js");
11222
+ const { cmdWorkspace } = await import("./workspace-BCA5RCHN.js");
10695
11223
  await cmdWorkspace(process.cwd(), ["workspace", "health", ...withoutWorkflowOnlyFlags(args)]);
10696
11224
  return;
10697
11225
  }
@@ -10724,7 +11252,7 @@ async function cmdVerifyWorkflow(args) {
10724
11252
  }
10725
11253
  let guardExitCode;
10726
11254
  if (brownfield) {
10727
- const { cmdHeal, collectCheckIssues } = await import("./heal-2UCSPRTK.js");
11255
+ const { cmdHeal, collectCheckIssues } = await import("./heal-WMSAX4LU.js");
10728
11256
  if (quietOutput) {
10729
11257
  const result = collectCheckIssues(workspaceInfo.appRoot, { brownfield: true });
10730
11258
  guardExitCode = result.issues.some((issue) => issue.type === "error") ? 1 : void 0;
@@ -10734,7 +11262,7 @@ async function cmdVerifyWorkflow(args) {
10734
11262
  process.exitCode = void 0;
10735
11263
  }
10736
11264
  }
10737
- const { cmdHealth, parseHealthArgs } = await import("./health-VAYRQVTF.js");
11265
+ const { cmdHealth, parseHealthArgs } = await import("./health-DBCUR6VQ.js");
10738
11266
  await cmdHealth(workspaceInfo.appRoot, parseHealthArgs(healthArgs));
10739
11267
  if (localPatterns) {
10740
11268
  const validation = validateLocalLaw(workspaceInfo.appRoot);
@@ -10784,14 +11312,22 @@ async function cmdVerifyWorkflow(args) {
10784
11312
  }
10785
11313
  }
10786
11314
  }
11315
+ const styleBridge = createStyleBridgeTaskSummary(workspaceInfo.appRoot);
11316
+ if (!quietOutput && styleBridge.path) {
11317
+ console.log("");
11318
+ console.log(`${GREEN16}Style bridge found:${RESET16} ${styleBridge.path}`);
11319
+ console.log(
11320
+ `${DIM16}${styleBridge.mappingCount} mapping(s), ${styleBridge.stylingApproach ?? "unknown"} styling${styleBridge.themeModes.length > 0 ? `, themes: ${styleBridge.themeModes.join(", ")}` : ""}${RESET16}`
11321
+ );
11322
+ }
10787
11323
  if (guardExitCode && guardExitCode !== 0 && (!process.exitCode || process.exitCode === 0)) {
10788
11324
  process.exitCode = guardExitCode;
10789
11325
  }
10790
11326
  }
10791
11327
  function readJsonIfPresent(path) {
10792
- if (!existsSync29(path)) return null;
11328
+ if (!existsSync30(path)) return null;
10793
11329
  try {
10794
- return JSON.parse(readFileSync22(path, "utf-8"));
11330
+ return JSON.parse(readFileSync23(path, "utf-8"));
10795
11331
  } catch {
10796
11332
  return null;
10797
11333
  }
@@ -10803,6 +11339,7 @@ function mentionsWord(text, term) {
10803
11339
  function createTaskAuthoritySummary(input) {
10804
11340
  const detected = detectProject(input.projectRoot);
10805
11341
  const hasLocalLaw = input.hasLocalPatterns || input.hasLocalRules;
11342
+ const hasStyleBridge = input.hasStyleBridge || input.adoptionMode === "style-bridge";
10806
11343
  let lane = "Brownfield contract-only";
10807
11344
  let sourceAuthority = "Existing app is authoritative; Decantr supplies contract context.";
10808
11345
  let styleAuthority = "Use the existing styling system.";
@@ -10815,11 +11352,11 @@ function createTaskAuthoritySummary(input) {
10815
11352
  sourceAuthority = "Existing app remains authoritative except where Decantr CSS is explicitly adopted.";
10816
11353
  styleAuthority = "Decantr CSS runtime is active where adopted.";
10817
11354
  activeAuthorities.push("Decantr CSS runtime");
10818
- } else if (input.workflowMode === "brownfield-attach" && input.adoptionMode === "style-bridge") {
11355
+ } else if (input.workflowMode === "brownfield-attach" && hasStyleBridge) {
10819
11356
  lane = "Hybrid style bridge";
10820
11357
  sourceAuthority = "Existing app remains authoritative; Decantr intent maps through the style bridge.";
10821
11358
  styleAuthority = "Use bridge tokens/classes as a mapping layer onto the app styling system.";
10822
- activeAuthorities.push("style bridge");
11359
+ activeAuthorities.push("accepted style bridge");
10823
11360
  } else if (input.workflowMode === "brownfield-attach" && hasLocalLaw) {
10824
11361
  lane = "Hybrid local law";
10825
11362
  sourceAuthority = "Existing app plus accepted project-owned UI law are authoritative.";
@@ -10888,7 +11425,7 @@ async function cmdTaskWorkflow(args) {
10888
11425
  }
10889
11426
  const route = routeInput.startsWith("/") ? routeInput : `/${routeInput}`;
10890
11427
  const taskSummary = positional.slice(1).join(" ").trim();
10891
- const essencePath = join31(workspaceInfo.appRoot, "decantr.essence.json");
11428
+ const essencePath = join32(workspaceInfo.appRoot, "decantr.essence.json");
10892
11429
  const essence = readJsonIfPresent(essencePath);
10893
11430
  if (!essence) {
10894
11431
  console.error(
@@ -10912,16 +11449,22 @@ async function cmdTaskWorkflow(args) {
10912
11449
  }
10913
11450
  const section = essence.blueprint.sections.find((entry) => entry.id === target.section);
10914
11451
  const page = section?.pages.find((entry) => entry.id === target.page);
10915
- const contextDir = join31(workspaceInfo.appRoot, ".decantr", "context");
10916
- const manifest = readJsonIfPresent(join31(contextDir, "pack-manifest.json"));
10917
- const projectJson = readJsonIfPresent(join31(workspaceInfo.appRoot, ".decantr", "project.json"));
11452
+ const contextDir = join32(workspaceInfo.appRoot, ".decantr", "context");
11453
+ const manifest = readJsonIfPresent(join32(contextDir, "pack-manifest.json"));
11454
+ const projectJson = readJsonIfPresent(join32(workspaceInfo.appRoot, ".decantr", "project.json"));
10918
11455
  const pagePack = manifest?.pages?.find((entry) => entry.id === target.page);
10919
11456
  const sectionPack = manifest?.sections?.find((entry) => entry.id === target.section);
10920
- const visualManifest = readJsonIfPresent(join31(workspaceInfo.appRoot, ".decantr", "evidence", "visual-manifest.json"));
11457
+ const visualManifest = readJsonIfPresent(join32(workspaceInfo.appRoot, ".decantr", "evidence", "visual-manifest.json"));
10921
11458
  const screenshot = visualManifest?.routes?.find((entry) => entry.route === route)?.screenshot;
10922
11459
  const localPatternPackPath = localPatternsPath(workspaceInfo.appRoot);
10923
11460
  const localRuleManifestPath = localRulesPath(workspaceInfo.appRoot);
11461
+ const acceptedStyleBridgePath = styleBridgePath(workspaceInfo.appRoot);
10924
11462
  const localLaw = createLocalLawTaskSummary(workspaceInfo.appRoot);
11463
+ const styleBridge = createStyleBridgeTaskSummary(workspaceInfo.appRoot);
11464
+ const displayedStyleBridge = {
11465
+ ...styleBridge,
11466
+ path: styleBridge.path ? displayProjectPath(workspaceInfo, styleBridge.path) : null
11467
+ };
10925
11468
  const displayedLocalLaw = {
10926
11469
  ...localLaw,
10927
11470
  patternsPath: localLaw.patternsPath ? displayProjectPath(workspaceInfo, localLaw.patternsPath) : null,
@@ -10934,10 +11477,11 @@ async function cmdTaskWorkflow(args) {
10934
11477
  projectRoot: workspaceInfo.appRoot,
10935
11478
  workflowMode: projectJson?.initialized?.workflowMode ?? null,
10936
11479
  adoptionMode: projectJson?.initialized?.adoptionMode ?? null,
10937
- hasLocalPatterns: existsSync29(localPatternPackPath),
10938
- hasLocalRules: existsSync29(localRuleManifestPath),
11480
+ hasLocalPatterns: existsSync30(localPatternPackPath),
11481
+ hasLocalRules: existsSync30(localRuleManifestPath),
10939
11482
  hasPackManifest: Boolean(manifest),
10940
- taskSummary
11483
+ taskSummary,
11484
+ hasStyleBridge: existsSync30(acceptedStyleBridgePath)
10941
11485
  });
10942
11486
  const context = {
10943
11487
  route,
@@ -10947,17 +11491,19 @@ async function cmdTaskWorkflow(args) {
10947
11491
  shell: page?.shell ?? section?.shell ?? null,
10948
11492
  patterns: page?.layout?.map(extractPatternName) ?? [],
10949
11493
  read: [
10950
- pagePack ? displayProjectPath(workspaceInfo, join31(".decantr/context", pagePack.markdown)) : null,
10951
- sectionPack ? displayProjectPath(workspaceInfo, join31(".decantr/context", sectionPack.markdown)) : null,
10952
- manifest?.scaffold?.markdown ? displayProjectPath(workspaceInfo, join31(".decantr/context", manifest.scaffold.markdown)) : null,
11494
+ pagePack ? displayProjectPath(workspaceInfo, join32(".decantr/context", pagePack.markdown)) : null,
11495
+ sectionPack ? displayProjectPath(workspaceInfo, join32(".decantr/context", sectionPack.markdown)) : null,
11496
+ manifest?.scaffold?.markdown ? displayProjectPath(workspaceInfo, join32(".decantr/context", manifest.scaffold.markdown)) : null,
10953
11497
  displayProjectPath(workspaceInfo, ".decantr/context/scaffold.md"),
10954
11498
  displayProjectPath(workspaceInfo, "DECANTR.md"),
10955
- existsSync29(localPatternPackPath) ? displayProjectPath(workspaceInfo, ".decantr/local-patterns.json") : null,
10956
- existsSync29(localRuleManifestPath) ? displayProjectPath(workspaceInfo, ".decantr/rules.json") : null
11499
+ existsSync30(localPatternPackPath) ? displayProjectPath(workspaceInfo, ".decantr/local-patterns.json") : null,
11500
+ existsSync30(localRuleManifestPath) ? displayProjectPath(workspaceInfo, ".decantr/rules.json") : null,
11501
+ existsSync30(acceptedStyleBridgePath) ? displayProjectPath(workspaceInfo, ".decantr/style-bridge.json") : null
10957
11502
  ].filter(Boolean),
10958
11503
  screenshot: screenshot?.startsWith(".decantr/") ? displayProjectPath(workspaceInfo, screenshot) : screenshot ?? null,
10959
11504
  authority,
10960
11505
  localLaw: displayedLocalLaw,
11506
+ styleBridge: displayedStyleBridge,
10961
11507
  changedFiles: currentChangedFiles,
10962
11508
  changedRoutes,
10963
11509
  verifyCommand: withProject("decantr verify --brownfield --local-patterns", projectArg)
@@ -11014,6 +11560,20 @@ async function cmdTaskWorkflow(args) {
11014
11560
  ` ${YELLOW12}Not codified yet.${RESET16} Run ${cyan3(withProject("decantr codify --from-audit", projectArg))} after adoption.`
11015
11561
  );
11016
11562
  }
11563
+ if (context.styleBridge.path) {
11564
+ console.log("");
11565
+ console.log(`${BOLD9}Project-owned style bridge:${RESET16}`);
11566
+ console.log(
11567
+ ` Bridge: ${cyan3(context.styleBridge.path)} (${context.styleBridge.mappingCount} mappings, ${context.styleBridge.stylingApproach ?? "unknown"} styling)`
11568
+ );
11569
+ if (context.styleBridge.themeModes.length > 0) {
11570
+ console.log(` Theme modes: ${context.styleBridge.themeModes.join(", ")}`);
11571
+ }
11572
+ for (const mapping of context.styleBridge.mappings.slice(0, 4)) {
11573
+ const hints = [...mapping.tokenHints, ...mapping.classHints].slice(0, 4).join(", ");
11574
+ console.log(` ${mapping.id}: ${mapping.label}${hints ? ` \u2014 ${hints}` : ""}`);
11575
+ }
11576
+ }
11017
11577
  if (context.changedFiles.length > 0) {
11018
11578
  console.log("");
11019
11579
  console.log(`${BOLD9}Changed-file context:${RESET16}`);
@@ -11039,7 +11599,14 @@ async function cmdCodifyWorkflow(args) {
11039
11599
  const projectArg = flagString(flags, "project");
11040
11600
  if (!ensureAllowedFlags(
11041
11601
  flags,
11042
- ["project", "from-audit", "discover-local-patterns", "codify-local-patterns", "accept"],
11602
+ [
11603
+ "project",
11604
+ "from-audit",
11605
+ "discover-local-patterns",
11606
+ "codify-local-patterns",
11607
+ "style-bridge",
11608
+ "accept"
11609
+ ],
11043
11610
  "codify"
11044
11611
  )) {
11045
11612
  return;
@@ -11047,25 +11614,29 @@ async function cmdCodifyWorkflow(args) {
11047
11614
  const workspaceInfo = resolveWorkflowProject(flags, "codify");
11048
11615
  if (!workspaceInfo) return;
11049
11616
  if (flagBoolean(flags, "accept")) {
11050
- if (!existsSync29(localPatternsProposalPath(workspaceInfo.appRoot)) && !existsSync29(localRulesProposalPath(workspaceInfo.appRoot))) {
11617
+ if (!existsSync30(localPatternsProposalPath(workspaceInfo.appRoot)) && !existsSync30(localRulesProposalPath(workspaceInfo.appRoot)) && !existsSync30(styleBridgeProposalPath(workspaceInfo.appRoot))) {
11051
11618
  console.error(
11052
11619
  error2(
11053
- "No local law proposal found. Run `decantr codify --from-audit` or `decantr codify` first."
11620
+ "No codify proposal found. Run `decantr codify --from-audit`, `decantr codify --style-bridge`, or `decantr codify` first."
11054
11621
  )
11055
11622
  );
11056
11623
  process.exitCode = 1;
11057
11624
  return;
11058
11625
  }
11059
- const result2 = acceptBrownfieldLocalLaw(workspaceInfo.appRoot);
11060
- if (result2.patternAcceptedPath) {
11061
- console.log(success3(`Accepted local pattern pack: ${result2.patternAcceptedPath}`));
11626
+ const result = acceptBrownfieldLocalLaw(workspaceInfo.appRoot);
11627
+ const bridgeAcceptedPath = acceptStyleBridge(workspaceInfo.appRoot);
11628
+ if (result.patternAcceptedPath) {
11629
+ console.log(success3(`Accepted local pattern pack: ${result.patternAcceptedPath}`));
11062
11630
  }
11063
- if (result2.rulesAcceptedPath) {
11064
- console.log(success3(`Accepted local rule manifest: ${result2.rulesAcceptedPath}`));
11631
+ if (result.rulesAcceptedPath) {
11632
+ console.log(success3(`Accepted local rule manifest: ${result.rulesAcceptedPath}`));
11633
+ }
11634
+ if (bridgeAcceptedPath) {
11635
+ console.log(success3(`Accepted style bridge: ${bridgeAcceptedPath}`));
11065
11636
  }
11066
11637
  console.log(
11067
11638
  dim3(
11068
- "Hybrid local law is now active: Decantr will treat accepted local patterns and rules as project-owned authority in task and verify flows."
11639
+ bridgeAcceptedPath ? "Hybrid style bridge is now active: Decantr will map design intent through accepted project tokens/classes in task, doctor, suggest, and CI flows." : "Hybrid local law is now active: Decantr will treat accepted local patterns and rules as project-owned authority in task and verify flows."
11069
11640
  )
11070
11641
  );
11071
11642
  console.log(
@@ -11077,19 +11648,42 @@ async function cmdCodifyWorkflow(args) {
11077
11648
  }
11078
11649
  const detected = detectProject(workspaceInfo.appRoot);
11079
11650
  const essence = readJsonIfPresent(
11080
- join31(workspaceInfo.appRoot, "decantr.essence.json")
11651
+ join32(workspaceInfo.appRoot, "decantr.essence.json")
11081
11652
  );
11082
11653
  const fromAudit = flagBoolean(flags, "from-audit") || flagBoolean(flags, "discover-local-patterns") || flagBoolean(flags, "codify-local-patterns");
11083
- const proposal = createBrownfieldCodifyProposal({
11084
- projectRoot: workspaceInfo.appRoot,
11085
- detected,
11086
- essence,
11087
- fromAudit
11088
- });
11089
- const result = writeBrownfieldCodifyProposal(workspaceInfo.appRoot, proposal);
11090
- console.log(success3(`Wrote local pattern proposal: ${result.patternPath}`));
11091
- console.log(success3(`Wrote local rule proposal: ${result.rulesPath}`));
11092
- if (fromAudit) {
11654
+ const wantsStyleBridge = flagBoolean(flags, "style-bridge");
11655
+ const wantsLocalLaw = !wantsStyleBridge || fromAudit;
11656
+ let localResult = null;
11657
+ if (wantsLocalLaw) {
11658
+ const proposal = createBrownfieldCodifyProposal({
11659
+ projectRoot: workspaceInfo.appRoot,
11660
+ detected,
11661
+ essence,
11662
+ fromAudit
11663
+ });
11664
+ localResult = writeBrownfieldCodifyProposal(workspaceInfo.appRoot, proposal);
11665
+ }
11666
+ let styleBridgeResult = null;
11667
+ if (wantsStyleBridge) {
11668
+ const styling = scanStyling(workspaceInfo.appRoot);
11669
+ styleBridgeResult = writeStyleBridgeProposal(
11670
+ workspaceInfo.appRoot,
11671
+ createStyleBridgeProposal({
11672
+ projectRoot: workspaceInfo.appRoot,
11673
+ detected,
11674
+ essence,
11675
+ styling
11676
+ })
11677
+ );
11678
+ }
11679
+ if (localResult) {
11680
+ console.log(success3(`Wrote local pattern proposal: ${localResult.patternPath}`));
11681
+ console.log(success3(`Wrote local rule proposal: ${localResult.rulesPath}`));
11682
+ }
11683
+ if (styleBridgeResult) {
11684
+ console.log(success3(`Wrote style bridge proposal: ${styleBridgeResult}`));
11685
+ }
11686
+ if (fromAudit && localResult) {
11093
11687
  console.log(
11094
11688
  dim3(
11095
11689
  "Proposal includes source-derived component candidates, starter mechanical rules, and Hybrid authority guidance."
@@ -11098,7 +11692,7 @@ async function cmdCodifyWorkflow(args) {
11098
11692
  }
11099
11693
  console.log(
11100
11694
  dim3(
11101
- `Review both files, add real component paths/token recipes, map hosted pattern ideas into project-owned law, then run \`${withProject("decantr codify --accept", projectArg)}\`.`
11695
+ `Review the proposal files, add real component paths/token/class recipes, map hosted pattern ideas into project-owned law, then run \`${withProject("decantr codify --accept", projectArg)}\`.`
11102
11696
  )
11103
11697
  );
11104
11698
  }
@@ -11153,7 +11747,7 @@ ${BOLD9}Usage:${RESET16}
11153
11747
  decantr verify [--project <path>] [--brownfield] [--local-patterns] [health options]
11154
11748
  decantr ci [--project <path>] [--workspace] [--fail-on error|warn|none]
11155
11749
  decantr doctor [--project <path>] [--workspace] [--json]
11156
- decantr codify [--from-audit] [--accept] [--project <path>]
11750
+ decantr codify [--from-audit] [--style-bridge] [--accept] [--project <path>]
11157
11751
  decantr studio [--port 4319] [--host 127.0.0.1] [--report decantr-health.json] [--workspace]
11158
11752
 
11159
11753
  ${BOLD9}Advanced primitives:${RESET16}
@@ -11573,15 +12167,17 @@ ${BOLD9}Examples:${RESET16}
11573
12167
  }
11574
12168
  function cmdCodifyHelp() {
11575
12169
  console.log(`
11576
- ${BOLD9}decantr codify${RESET16} \u2014 Propose or accept project-owned Brownfield UI patterns and rules
12170
+ ${BOLD9}decantr codify${RESET16} \u2014 Propose or accept project-owned Brownfield UI law and style bridges
11577
12171
 
11578
12172
  ${BOLD9}Usage:${RESET16}
11579
- decantr codify [--from-audit] [--project <path>]
12173
+ decantr codify [--from-audit] [--style-bridge] [--project <path>]
11580
12174
  decantr codify --accept [--project <path>]
11581
12175
 
11582
12176
  ${BOLD9}Examples:${RESET16}
11583
12177
  decantr codify
11584
12178
  decantr codify --from-audit
12179
+ decantr codify --style-bridge
12180
+ decantr codify --from-audit --style-bridge
11585
12181
  decantr codify --accept
11586
12182
  decantr verify --brownfield --local-patterns
11587
12183
  `);
@@ -11664,10 +12260,10 @@ async function main() {
11664
12260
  if (command === "--version" || command === "-v" || command === "version") {
11665
12261
  try {
11666
12262
  const here = dirname6(fileURLToPath3(import.meta.url));
11667
- const candidates = [join31(here, "..", "package.json"), join31(here, "..", "..", "package.json")];
12263
+ const candidates = [join32(here, "..", "package.json"), join32(here, "..", "..", "package.json")];
11668
12264
  for (const candidate of candidates) {
11669
- if (existsSync29(candidate)) {
11670
- const pkg = JSON.parse(readFileSync22(candidate, "utf-8"));
12265
+ if (existsSync30(candidate)) {
12266
+ const pkg = JSON.parse(readFileSync23(candidate, "utf-8"));
11671
12267
  if (pkg.version) {
11672
12268
  console.log(pkg.version);
11673
12269
  return;
@@ -11820,7 +12416,7 @@ async function main() {
11820
12416
  `${YELLOW12}Note: \`decantr heal\` is deprecated. Use \`decantr check\` instead.${RESET16}`
11821
12417
  );
11822
12418
  }
11823
- const { cmdHeal } = await import("./heal-2UCSPRTK.js");
12419
+ const { cmdHeal } = await import("./heal-WMSAX4LU.js");
11824
12420
  const { flags } = parseLooseArgs(args);
11825
12421
  const workspaceInfo = resolveWorkflowProject(flags, "check");
11826
12422
  if (!workspaceInfo) break;
@@ -11845,7 +12441,7 @@ async function main() {
11845
12441
  const { flags } = parseLooseArgs(args);
11846
12442
  const workspaceInfo = resolveWorkflowProject(flags, "health");
11847
12443
  if (!workspaceInfo) break;
11848
- const { cmdHealth, parseHealthArgs } = await import("./health-VAYRQVTF.js");
12444
+ const { cmdHealth, parseHealthArgs } = await import("./health-DBCUR6VQ.js");
11849
12445
  await cmdHealth(workspaceInfo.appRoot, parseHealthArgs(stripProjectArgs(args)));
11850
12446
  } catch (e) {
11851
12447
  console.error(error2(e.message));
@@ -11873,7 +12469,7 @@ async function main() {
11873
12469
  cmdStudioHelp();
11874
12470
  break;
11875
12471
  }
11876
- const { cmdStudio, parseStudioArgs } = await import("./studio-WPETOZRW.js");
12472
+ const { cmdStudio, parseStudioArgs } = await import("./studio-L6JJQYCS.js");
11877
12473
  await cmdStudio(process.cwd(), parseStudioArgs(args));
11878
12474
  } catch (e) {
11879
12475
  console.error(error2(e.message));
@@ -11887,7 +12483,7 @@ async function main() {
11887
12483
  cmdWorkspaceHelp();
11888
12484
  break;
11889
12485
  }
11890
- const { cmdWorkspace } = await import("./workspace-PBO3BJRL.js");
12486
+ const { cmdWorkspace } = await import("./workspace-BCA5RCHN.js");
11891
12487
  await cmdWorkspace(process.cwd(), args);
11892
12488
  } catch (e) {
11893
12489
  console.error(error2(e.message));
@@ -12004,7 +12600,7 @@ async function main() {
12004
12600
  route,
12005
12601
  file,
12006
12602
  fromCode,
12007
- projectRoot: workspaceInfo?.appRoot
12603
+ projectRoot: workspaceInfo?.appRoot ?? process.cwd()
12008
12604
  });
12009
12605
  break;
12010
12606
  }
@@ -12206,7 +12802,7 @@ async function main() {
12206
12802
  break;
12207
12803
  }
12208
12804
  if (packType === "page" && route && !id) {
12209
- const resolvedPath = essencePath ? resolveUserPath(essencePath) : join31(process.cwd(), "decantr.essence.json");
12805
+ const resolvedPath = essencePath ? resolveUserPath(essencePath) : join32(process.cwd(), "decantr.essence.json");
12210
12806
  id = resolvePagePackIdForRoute(resolvedPath, route);
12211
12807
  }
12212
12808
  await printHostedSelectedExecutionPack(