@decantr/cli 2.5.0 → 2.6.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.
@@ -20,6 +20,8 @@ import {
20
20
  createDoctrineMap,
21
21
  getCliTelemetryIdentityStatus,
22
22
  isOptedIn,
23
+ loadBundledContentItem,
24
+ loadBundledContentList,
23
25
  optIn,
24
26
  scanAmbientContext,
25
27
  scanProjectInteractions,
@@ -29,11 +31,11 @@ import {
29
31
  sendCliCommandTelemetry,
30
32
  sendNewProjectCompletedTelemetry,
31
33
  writeDoctrineMap
32
- } from "./chunk-IEW2QFYI.js";
34
+ } from "./chunk-KT2ROK2D.js";
33
35
 
34
36
  // src/index.ts
35
- import { existsSync as existsSync26, mkdirSync as mkdirSync12, readdirSync as readdirSync6, readFileSync as readFileSync19, writeFileSync as writeFileSync15 } from "fs";
36
- import { basename as basename2, dirname as dirname4, isAbsolute, join as join27, resolve as resolve4 } from "path";
37
+ import { existsSync as existsSync27, mkdirSync as mkdirSync13, readdirSync as readdirSync7, readFileSync as readFileSync20, writeFileSync as writeFileSync16 } from "fs";
38
+ import { basename as basename2, dirname as dirname4, isAbsolute, join as join28, resolve as resolve4 } from "path";
37
39
  import { fileURLToPath as fileURLToPath2 } from "url";
38
40
  import { evaluateGuard, isV4 as isV47, validateEssence as validateEssence2 } from "@decantr/essence-spec";
39
41
  import {
@@ -45,7 +47,9 @@ import {
45
47
  isContentType as isGetContentType,
46
48
  isPublicBlueprintSet,
47
49
  API_CONTENT_TYPES as LIST_CONTENT_TYPES,
48
- RegistryAPIClient as RegistryAPIClient3
50
+ patternToDiscoveryCandidate,
51
+ RegistryAPIClient as RegistryAPIClient3,
52
+ rankPatternCandidates
49
53
  } from "@decantr/registry";
50
54
  import {
51
55
  auditProject,
@@ -1819,8 +1823,8 @@ async function cmdAddFeature(feature, args, projectRoot = process.cwd()) {
1819
1823
  }
1820
1824
 
1821
1825
  // src/commands/analyze.ts
1822
- import { existsSync as existsSync11, mkdirSync as mkdirSync5, writeFileSync as writeFileSync6 } from "fs";
1823
- import { join as join12 } from "path";
1826
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
1827
+ import { join as join13 } from "path";
1824
1828
 
1825
1829
  // src/analyzers/components.ts
1826
1830
  import { existsSync as existsSync5, readdirSync, statSync as statSync2 } from "fs";
@@ -2346,9 +2350,262 @@ function scanLayout(projectRoot) {
2346
2350
  };
2347
2351
  }
2348
2352
 
2353
+ // src/brownfield-intelligence.ts
2354
+ import {
2355
+ existsSync as existsSync9,
2356
+ mkdirSync as mkdirSync5,
2357
+ readdirSync as readdirSync4,
2358
+ readFileSync as readFileSync8,
2359
+ statSync as statSync4,
2360
+ writeFileSync as writeFileSync6
2361
+ } from "fs";
2362
+ import { join as join10, relative } from "path";
2363
+ var CSS_SCAN_DIRS = ["src", "app", "styles", "assets"];
2364
+ var CSS_MAX_FILE_SIZE = 1024 * 1024;
2365
+ function collectCssFiles(projectRoot) {
2366
+ const files = [];
2367
+ const seen = /* @__PURE__ */ new Set();
2368
+ const skip = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", ".decantr", "dist", "build", "coverage"]);
2369
+ const walk = (dir) => {
2370
+ let entries;
2371
+ try {
2372
+ entries = readdirSync4(dir, { withFileTypes: true });
2373
+ } catch {
2374
+ return;
2375
+ }
2376
+ for (const entry of entries) {
2377
+ if (skip.has(entry.name)) continue;
2378
+ const full = join10(dir, entry.name);
2379
+ if (entry.isDirectory()) {
2380
+ walk(full);
2381
+ continue;
2382
+ }
2383
+ if (!entry.isFile() || !entry.name.endsWith(".css")) continue;
2384
+ try {
2385
+ if (statSync4(full).size > CSS_MAX_FILE_SIZE) continue;
2386
+ } catch {
2387
+ continue;
2388
+ }
2389
+ if (!seen.has(full)) {
2390
+ files.push(full);
2391
+ seen.add(full);
2392
+ }
2393
+ }
2394
+ };
2395
+ for (const dir of CSS_SCAN_DIRS.map((segment) => join10(projectRoot, segment))) {
2396
+ if (existsSync9(dir)) walk(dir);
2397
+ }
2398
+ return files.sort();
2399
+ }
2400
+ function selectorFromMatch(raw) {
2401
+ return raw.replace(/\s+/g, " ").trim().slice(0, 120);
2402
+ }
2403
+ function createThemeInventory(projectRoot, styling) {
2404
+ const files = collectCssFiles(projectRoot);
2405
+ const variantMap = /* @__PURE__ */ new Map();
2406
+ const notes = [];
2407
+ const ensureVariant = (id, selector, evidence) => {
2408
+ const existing = variantMap.get(id) ?? {
2409
+ id,
2410
+ selectors: [],
2411
+ tokenCount: 0,
2412
+ colorTokenCount: 0,
2413
+ evidence: []
2414
+ };
2415
+ if (!existing.selectors.includes(selector)) existing.selectors.push(selector);
2416
+ if (!existing.evidence.includes(evidence)) existing.evidence.push(evidence);
2417
+ variantMap.set(id, existing);
2418
+ };
2419
+ for (const file of files) {
2420
+ let content = "";
2421
+ try {
2422
+ content = readFileSync8(file, "utf-8");
2423
+ } catch {
2424
+ continue;
2425
+ }
2426
+ const rel = relative(projectRoot, file).replace(/\\/g, "/");
2427
+ const selectorRegex = /((?:\.[\w-]*dark[\w-]*|\[data-theme=["'][^"']+["']\]|\[data-theme=[^\]]+\]|\[data-[\w-]*theme=["'][^"']+["']\]|html\.dark|:root|\.theme-[\w-]+)[^{]*)\{/g;
2428
+ for (const match of content.matchAll(selectorRegex)) {
2429
+ const selector = selectorFromMatch(match[1] ?? "");
2430
+ if (!selector) continue;
2431
+ let id = "base";
2432
+ const dataTheme = selector.match(/data-(?:[\w-]*theme|theme)=["']?([^"'\]\s]+)/i);
2433
+ const themeClass = selector.match(/\.theme-([a-z0-9-]+)/i);
2434
+ if (/dark/i.test(selector)) id = "dark";
2435
+ if (dataTheme?.[1]) id = dataTheme[1].toLowerCase();
2436
+ if (themeClass?.[1]) id = themeClass[1].toLowerCase();
2437
+ if (selector.includes(":root")) id = "base";
2438
+ ensureVariant(id, selector, rel);
2439
+ }
2440
+ for (const [id, entry] of variantMap) {
2441
+ const scopedSelectors = entry.selectors.filter((selector) => content.includes(selector));
2442
+ if (scopedSelectors.length === 0) continue;
2443
+ const scopedText = scopedSelectors.map((selector) => {
2444
+ const idx = content.indexOf(selector);
2445
+ return idx >= 0 ? content.slice(idx, Math.min(content.length, idx + 3e3)) : "";
2446
+ }).join("\n");
2447
+ const tokens = [...scopedText.matchAll(/--[\w-]+\s*:/g)].length;
2448
+ const colors = [
2449
+ ...scopedText.matchAll(
2450
+ /--[\w-]*(?:color|bg|fg|surface|border|accent|primary|secondary)[\w-]*\s*:/gi
2451
+ )
2452
+ ].length;
2453
+ variantMap.set(id, {
2454
+ ...entry,
2455
+ tokenCount: Math.max(entry.tokenCount, tokens),
2456
+ colorTokenCount: Math.max(entry.colorTokenCount, colors)
2457
+ });
2458
+ }
2459
+ }
2460
+ if (variantMap.size === 0) {
2461
+ ensureVariant("base", ":root or existing component styles", "detected styling inventory");
2462
+ notes.push(
2463
+ "No explicit theme selectors were found; Decantr will treat the current UI as the existing-app authority."
2464
+ );
2465
+ }
2466
+ if (styling.darkMode && !variantMap.has("dark")) {
2467
+ ensureVariant("dark", "dark mode signal", "styling analyzer");
2468
+ }
2469
+ const variants = [...variantMap.values()].sort((a, b) => a.id.localeCompare(b.id));
2470
+ const modes = variants.map((variant) => variant.id);
2471
+ if (variants.length > 2) {
2472
+ notes.push(
2473
+ "Multiple theme variants were observed. Essence V4 remains unchanged; variants are reported here for task-time context."
2474
+ );
2475
+ }
2476
+ return {
2477
+ version: 1,
2478
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2479
+ localOnly: true,
2480
+ stylingApproach: styling.approach,
2481
+ darkModeDetected: styling.darkMode,
2482
+ modes,
2483
+ variants,
2484
+ tokens: {
2485
+ cssVariables: styling.cssVariables,
2486
+ colors: styling.colors
2487
+ },
2488
+ notes
2489
+ };
2490
+ }
2491
+ function readScreenshotEvidence(projectRoot) {
2492
+ const dir = join10(projectRoot, ".decantr", "evidence", "screenshots");
2493
+ if (!existsSync9(dir)) return [];
2494
+ try {
2495
+ return readdirSync4(dir).filter((file) => file.endsWith(".png") || file.endsWith(".jpg") || file.endsWith(".jpeg")).map((file) => `.decantr/evidence/screenshots/${file}`).sort();
2496
+ } catch {
2497
+ return [];
2498
+ }
2499
+ }
2500
+ function backlogItems(input) {
2501
+ const items = [
2502
+ "Confirm the observed personality and visual target in `.decantr/observed-essence.proposal.json` before asking an AI agent to add net-new UI.",
2503
+ "Enrich high-traffic routes with precise directives: states, interactions, shared components, responsive behavior, and accessibility expectations.",
2504
+ "Run `decantr health --browser --base-url <url> --evidence` after the app is running to attach local screenshot evidence."
2505
+ ];
2506
+ if (input.routes.routes.length > 6) {
2507
+ items.push(
2508
+ "Group mature-app routes into stable zones so future tasks can load focused page or section context instead of the whole app."
2509
+ );
2510
+ }
2511
+ if (input.components.componentCount > 10) {
2512
+ items.push("Identify shared component surfaces and record where reuse beats duplication.");
2513
+ }
2514
+ if (input.themeInventory.variants.length > 1) {
2515
+ items.push(
2516
+ "Document which routes use each observed theme variant; do not promote variants into Essence until the contract boundary is reviewed."
2517
+ );
2518
+ }
2519
+ if (input.features.detected.includes("chat") || input.features.detected.includes("file-upload")) {
2520
+ items.push(
2521
+ "Enrich AI/chat/upload flows with concrete interaction evidence so agents do not replace product behavior with generic patterns."
2522
+ );
2523
+ }
2524
+ if (input.styling.approach !== "decantr-css") {
2525
+ items.push(
2526
+ `Keep Brownfield implementation in the existing ${input.styling.approach} styling system unless adoption mode changes.`
2527
+ );
2528
+ }
2529
+ if (input.layout.shellPattern === "main-only" && input.routes.routes.length > 3) {
2530
+ items.push(
2531
+ "Review shell ownership; the analyzer did not find a clear nav/sidebar/footer structure for a multi-route app."
2532
+ );
2533
+ }
2534
+ return items;
2535
+ }
2536
+ function renderBacklog(items) {
2537
+ return [
2538
+ "# Decantr Brownfield Enrichment Backlog",
2539
+ "",
2540
+ "Use this checklist to turn the first Brownfield attach pass into durable task-time context.",
2541
+ "",
2542
+ ...items.map((item) => `- [ ] ${item}`),
2543
+ ""
2544
+ ].join("\n");
2545
+ }
2546
+ function writeBrownfieldIntelligenceArtifacts(input) {
2547
+ const decantrDir = join10(input.projectRoot, ".decantr");
2548
+ mkdirSync5(decantrDir, { recursive: true });
2549
+ const themeInventory = createThemeInventory(input.projectRoot, input.styling);
2550
+ const screenshots = readScreenshotEvidence(input.projectRoot);
2551
+ const backlog = backlogItems({ ...input, themeInventory });
2552
+ const intelligence = {
2553
+ version: 1,
2554
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2555
+ localOnly: true,
2556
+ workflow: "brownfield-attach",
2557
+ essenceVersion: "4.0.0",
2558
+ project: {
2559
+ framework: input.project.framework,
2560
+ frameworkVersion: input.project.version ?? null,
2561
+ packageManager: input.project.packageManager,
2562
+ hasTypeScript: input.project.hasTypeScript,
2563
+ hasTailwind: input.project.hasTailwind,
2564
+ existingRuleFiles: input.project.existingRuleFiles
2565
+ },
2566
+ routeIntelligence: {
2567
+ strategy: input.routes.strategy,
2568
+ routeCount: input.routes.routes.length,
2569
+ routes: input.routes.routes.map((route) => ({
2570
+ path: route.path,
2571
+ file: route.file,
2572
+ hasLayout: route.hasLayout
2573
+ }))
2574
+ },
2575
+ componentSurface: {
2576
+ pageCount: input.components.pageCount,
2577
+ componentCount: input.components.componentCount,
2578
+ directories: input.components.directories
2579
+ },
2580
+ styling: {
2581
+ approach: input.styling.approach,
2582
+ configFile: input.styling.configFile ?? null,
2583
+ darkMode: input.styling.darkMode,
2584
+ cssVariableCount: input.styling.cssVariables.length,
2585
+ themeInventoryPath: ".decantr/theme-inventory.json"
2586
+ },
2587
+ layout: input.layout,
2588
+ features: input.features,
2589
+ dependencies: input.dependencies,
2590
+ evidence: {
2591
+ screenshotsLocalOnly: true,
2592
+ screenshots,
2593
+ visualManifestPath: existsSync9(join10(decantrDir, "evidence", "visual-manifest.json")) ? ".decantr/evidence/visual-manifest.json" : null
2594
+ },
2595
+ recommendedNextSteps: backlog
2596
+ };
2597
+ const intelligencePath = join10(decantrDir, "brownfield-intelligence.json");
2598
+ const themeInventoryPath = join10(decantrDir, "theme-inventory.json");
2599
+ const backlogPath = join10(decantrDir, "enrichment-backlog.md");
2600
+ writeFileSync6(intelligencePath, JSON.stringify(intelligence, null, 2) + "\n", "utf-8");
2601
+ writeFileSync6(themeInventoryPath, JSON.stringify(themeInventory, null, 2) + "\n", "utf-8");
2602
+ writeFileSync6(backlogPath, renderBacklog(backlog), "utf-8");
2603
+ return { intelligencePath, themeInventoryPath, backlogPath };
2604
+ }
2605
+
2349
2606
  // src/detect.ts
2350
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
2351
- import { join as join10 } from "path";
2607
+ import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
2608
+ import { join as join11 } from "path";
2352
2609
  var RULE_FILES = [
2353
2610
  "CLAUDE.md",
2354
2611
  ".claude/rules",
@@ -2370,52 +2627,52 @@ function detectProject(projectRoot = process.cwd()) {
2370
2627
  existingEssence: false,
2371
2628
  projectRoot
2372
2629
  };
2373
- result.existingEssence = existsSync9(join10(projectRoot, "decantr.essence.json"));
2630
+ result.existingEssence = existsSync10(join11(projectRoot, "decantr.essence.json"));
2374
2631
  for (const ruleFile of RULE_FILES) {
2375
- if (existsSync9(join10(projectRoot, ruleFile))) {
2632
+ if (existsSync10(join11(projectRoot, ruleFile))) {
2376
2633
  result.existingRuleFiles.push(ruleFile);
2377
2634
  }
2378
2635
  }
2379
- if (existsSync9(join10(projectRoot, "pnpm-lock.yaml"))) {
2636
+ if (existsSync10(join11(projectRoot, "pnpm-lock.yaml"))) {
2380
2637
  result.packageManager = "pnpm";
2381
- } else if (existsSync9(join10(projectRoot, "yarn.lock"))) {
2638
+ } else if (existsSync10(join11(projectRoot, "yarn.lock"))) {
2382
2639
  result.packageManager = "yarn";
2383
- } else if (existsSync9(join10(projectRoot, "bun.lockb"))) {
2640
+ } else if (existsSync10(join11(projectRoot, "bun.lockb"))) {
2384
2641
  result.packageManager = "bun";
2385
- } else if (existsSync9(join10(projectRoot, "package-lock.json"))) {
2642
+ } else if (existsSync10(join11(projectRoot, "package-lock.json"))) {
2386
2643
  result.packageManager = "npm";
2387
2644
  }
2388
- result.hasTypeScript = existsSync9(join10(projectRoot, "tsconfig.json"));
2389
- result.hasTailwind = existsSync9(join10(projectRoot, "tailwind.config.js")) || existsSync9(join10(projectRoot, "tailwind.config.ts")) || existsSync9(join10(projectRoot, "tailwind.config.mjs")) || existsSync9(join10(projectRoot, "tailwind.config.cjs"));
2390
- if (existsSync9(join10(projectRoot, "next.config.js")) || existsSync9(join10(projectRoot, "next.config.ts")) || existsSync9(join10(projectRoot, "next.config.mjs"))) {
2645
+ result.hasTypeScript = existsSync10(join11(projectRoot, "tsconfig.json"));
2646
+ result.hasTailwind = existsSync10(join11(projectRoot, "tailwind.config.js")) || existsSync10(join11(projectRoot, "tailwind.config.ts")) || existsSync10(join11(projectRoot, "tailwind.config.mjs")) || existsSync10(join11(projectRoot, "tailwind.config.cjs"));
2647
+ if (existsSync10(join11(projectRoot, "next.config.js")) || existsSync10(join11(projectRoot, "next.config.ts")) || existsSync10(join11(projectRoot, "next.config.mjs"))) {
2391
2648
  result.framework = "nextjs";
2392
2649
  result.version = getPackageVersion(projectRoot, "next");
2393
2650
  return result;
2394
2651
  }
2395
- if (existsSync9(join10(projectRoot, "nuxt.config.js")) || existsSync9(join10(projectRoot, "nuxt.config.ts"))) {
2652
+ if (existsSync10(join11(projectRoot, "nuxt.config.js")) || existsSync10(join11(projectRoot, "nuxt.config.ts"))) {
2396
2653
  result.framework = "nuxt";
2397
2654
  result.version = getPackageVersion(projectRoot, "nuxt");
2398
2655
  return result;
2399
2656
  }
2400
- if (existsSync9(join10(projectRoot, "astro.config.mjs")) || existsSync9(join10(projectRoot, "astro.config.ts"))) {
2657
+ if (existsSync10(join11(projectRoot, "astro.config.mjs")) || existsSync10(join11(projectRoot, "astro.config.ts"))) {
2401
2658
  result.framework = "astro";
2402
2659
  result.version = getPackageVersion(projectRoot, "astro");
2403
2660
  return result;
2404
2661
  }
2405
- if (existsSync9(join10(projectRoot, "svelte.config.js")) || existsSync9(join10(projectRoot, "svelte.config.ts"))) {
2662
+ if (existsSync10(join11(projectRoot, "svelte.config.js")) || existsSync10(join11(projectRoot, "svelte.config.ts"))) {
2406
2663
  result.framework = "svelte";
2407
2664
  result.version = getPackageVersion(projectRoot, "svelte");
2408
2665
  return result;
2409
2666
  }
2410
- if (existsSync9(join10(projectRoot, "angular.json"))) {
2667
+ if (existsSync10(join11(projectRoot, "angular.json"))) {
2411
2668
  result.framework = "angular";
2412
2669
  result.version = getPackageVersion(projectRoot, "@angular/core");
2413
2670
  return result;
2414
2671
  }
2415
- const packageJsonPath = join10(projectRoot, "package.json");
2416
- if (existsSync9(packageJsonPath)) {
2672
+ const packageJsonPath = join11(projectRoot, "package.json");
2673
+ if (existsSync10(packageJsonPath)) {
2417
2674
  try {
2418
- const packageJson = JSON.parse(readFileSync8(packageJsonPath, "utf-8"));
2675
+ const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
2419
2676
  const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
2420
2677
  if (deps.next) {
2421
2678
  result.framework = "nextjs";
@@ -2445,18 +2702,18 @@ function detectProject(projectRoot = process.cwd()) {
2445
2702
  } catch {
2446
2703
  }
2447
2704
  }
2448
- if (result.framework === "unknown" && !existsSync9(packageJsonPath)) {
2449
- if (existsSync9(join10(projectRoot, "index.html"))) {
2705
+ if (result.framework === "unknown" && !existsSync10(packageJsonPath)) {
2706
+ if (existsSync10(join11(projectRoot, "index.html"))) {
2450
2707
  result.framework = "html";
2451
2708
  }
2452
2709
  }
2453
2710
  return result;
2454
2711
  }
2455
2712
  function getPackageVersion(projectRoot, packageName) {
2456
- const packageJsonPath = join10(projectRoot, "package.json");
2457
- if (!existsSync9(packageJsonPath)) return void 0;
2713
+ const packageJsonPath = join11(projectRoot, "package.json");
2714
+ if (!existsSync10(packageJsonPath)) return void 0;
2458
2715
  try {
2459
- const packageJson = JSON.parse(readFileSync8(packageJsonPath, "utf-8"));
2716
+ const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
2460
2717
  const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
2461
2718
  const version = deps[packageName];
2462
2719
  return version?.replace(/^\^|~/, "");
@@ -2489,8 +2746,8 @@ function formatDetection(detected) {
2489
2746
  }
2490
2747
 
2491
2748
  // src/workflow-model.ts
2492
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
2493
- import { join as join11 } from "path";
2749
+ import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
2750
+ import { join as join12 } from "path";
2494
2751
  function inferSuggestedShell(layout) {
2495
2752
  if (layout.hasSidebar) return "sidebar-main";
2496
2753
  if (layout.hasTopNav) return "top-nav-main";
@@ -2577,12 +2834,12 @@ function createBrownfieldInitSeed(detected, layout, styling) {
2577
2834
  };
2578
2835
  }
2579
2836
  function readBrownfieldInitSeed(projectRoot) {
2580
- const seedPath = join11(projectRoot, ".decantr", "init-seed.json");
2581
- if (!existsSync10(seedPath)) {
2837
+ const seedPath = join12(projectRoot, ".decantr", "init-seed.json");
2838
+ if (!existsSync11(seedPath)) {
2582
2839
  return null;
2583
2840
  }
2584
2841
  try {
2585
- const parsed = JSON.parse(readFileSync9(seedPath, "utf-8"));
2842
+ const parsed = JSON.parse(readFileSync10(seedPath, "utf-8"));
2586
2843
  if (parsed.workflow !== "brownfield-adoption") {
2587
2844
  return null;
2588
2845
  }
@@ -2693,21 +2950,31 @@ ${BOLD}Analyzing project...${RESET2}
2693
2950
  ]
2694
2951
  }
2695
2952
  };
2696
- const decantrDir = join12(projectRoot, ".decantr");
2697
- if (!existsSync11(decantrDir)) {
2698
- mkdirSync5(decantrDir, { recursive: true });
2699
- }
2700
- const outputPath = join12(decantrDir, "analysis.json");
2701
- const initSeedPath = join12(decantrDir, "init-seed.json");
2702
- const ambientPath = join12(decantrDir, "ambient-context.json");
2703
- const doctrinePath = join12(decantrDir, "doctrine-map.json");
2704
- const reportPath = join12(decantrDir, "brownfield-report.md");
2705
- writeFileSync6(outputPath, JSON.stringify(analysis, null, 2) + "\n", "utf-8");
2706
- writeFileSync6(initSeedPath, JSON.stringify(initSeed, null, 2) + "\n", "utf-8");
2707
- writeFileSync6(ambientPath, JSON.stringify(ambient, null, 2) + "\n", "utf-8");
2953
+ const decantrDir = join13(projectRoot, ".decantr");
2954
+ if (!existsSync12(decantrDir)) {
2955
+ mkdirSync6(decantrDir, { recursive: true });
2956
+ }
2957
+ const outputPath = join13(decantrDir, "analysis.json");
2958
+ const initSeedPath = join13(decantrDir, "init-seed.json");
2959
+ const ambientPath = join13(decantrDir, "ambient-context.json");
2960
+ const doctrinePath = join13(decantrDir, "doctrine-map.json");
2961
+ const reportPath = join13(decantrDir, "brownfield-report.md");
2962
+ writeFileSync7(outputPath, JSON.stringify(analysis, null, 2) + "\n", "utf-8");
2963
+ writeFileSync7(initSeedPath, JSON.stringify(initSeed, null, 2) + "\n", "utf-8");
2964
+ writeFileSync7(ambientPath, JSON.stringify(ambient, null, 2) + "\n", "utf-8");
2708
2965
  writeDoctrineMap(projectRoot, doctrine);
2709
2966
  writeBrownfieldProposal(projectRoot, proposal);
2710
- writeFileSync6(reportPath, generateBrownfieldReport(proposal, ambient, doctrine), "utf-8");
2967
+ writeFileSync7(reportPath, generateBrownfieldReport(proposal, ambient, doctrine), "utf-8");
2968
+ const intelligenceArtifacts = writeBrownfieldIntelligenceArtifacts({
2969
+ projectRoot,
2970
+ project,
2971
+ routes,
2972
+ components,
2973
+ styling,
2974
+ layout,
2975
+ features,
2976
+ dependencies
2977
+ });
2711
2978
  console.log(`
2712
2979
  ${GREEN2}Analysis complete.${RESET2}
2713
2980
  `);
@@ -2743,8 +3010,13 @@ ${DIM2}Written to:${RESET2} ${outputPath}`);
2743
3010
  console.log(`${DIM2}Init seed:${RESET2} ${initSeedPath}`);
2744
3011
  console.log(`${DIM2}Ambient context:${RESET2} ${ambientPath}`);
2745
3012
  console.log(`${DIM2}Doctrine map:${RESET2} ${doctrinePath}`);
2746
- console.log(`${DIM2}Observed proposal:${RESET2} ${join12(decantrDir, "observed-essence.proposal.json")}`);
3013
+ console.log(
3014
+ `${DIM2}Observed proposal:${RESET2} ${join13(decantrDir, "observed-essence.proposal.json")}`
3015
+ );
2747
3016
  console.log(`${DIM2}Brownfield report:${RESET2} ${reportPath}`);
3017
+ console.log(`${DIM2}Brownfield intelligence:${RESET2} ${intelligenceArtifacts.intelligencePath}`);
3018
+ console.log(`${DIM2}Theme inventory:${RESET2} ${intelligenceArtifacts.themeInventoryPath}`);
3019
+ console.log(`${DIM2}Enrichment backlog:${RESET2} ${intelligenceArtifacts.backlogPath}`);
2748
3020
  console.log(
2749
3021
  `
2750
3022
  ${YELLOW}Next step:${RESET2} Review ${BOLD}.decantr/brownfield-report.md${RESET2}, then run ${BOLD}decantr init --existing --accept-proposal${RESET2} to attach Decantr using the observed proposal.
@@ -2769,8 +3041,8 @@ ${YELLOW}Next step:${RESET2} Review ${BOLD}.decantr/brownfield-report.md${RESET2
2769
3041
  }
2770
3042
 
2771
3043
  // src/commands/create.ts
2772
- import { existsSync as existsSync12, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
2773
- import { join as join13 } from "path";
3044
+ import { existsSync as existsSync13, mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
3045
+ import { join as join14 } from "path";
2774
3046
  import {
2775
3047
  CONTENT_TYPE_TO_API_CONTENT_TYPE,
2776
3048
  CONTENT_TYPES
@@ -2966,23 +3238,23 @@ function cmdCreate(type, name, projectRoot = process.cwd()) {
2966
3238
  }
2967
3239
  const contentType = type;
2968
3240
  const plural = PLURAL[contentType];
2969
- const customDir = join13(projectRoot, ".decantr", "custom", plural);
2970
- const filePath = join13(customDir, `${name}.json`);
2971
- if (existsSync12(filePath)) {
3241
+ const customDir = join14(projectRoot, ".decantr", "custom", plural);
3242
+ const filePath = join14(customDir, `${name}.json`);
3243
+ if (existsSync13(filePath)) {
2972
3244
  console.error(`${type} "${name}" already exists at ${filePath}`);
2973
3245
  process.exitCode = 1;
2974
3246
  return;
2975
3247
  }
2976
- mkdirSync6(customDir, { recursive: true });
3248
+ mkdirSync7(customDir, { recursive: true });
2977
3249
  const skeleton = getSkeleton(contentType, name, humanizeId(name));
2978
- writeFileSync7(filePath, JSON.stringify(skeleton, null, 2));
3250
+ writeFileSync8(filePath, JSON.stringify(skeleton, null, 2));
2979
3251
  console.log(`Created ${type} "${name}" at ${filePath}`);
2980
3252
  console.log(`Edit it, then publish with: decantr publish ${type} ${name}`);
2981
3253
  }
2982
3254
 
2983
3255
  // src/commands/export.ts
2984
- import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
2985
- import { dirname as dirname2, join as join14 } from "path";
3256
+ import { existsSync as existsSync14, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
3257
+ import { dirname as dirname2, join as join15 } from "path";
2986
3258
  var GREEN3 = "\x1B[32m";
2987
3259
  var RED2 = "\x1B[31m";
2988
3260
  var DIM3 = "\x1B[2m";
@@ -3148,21 +3420,21 @@ function generateFigmaTokens(tokens) {
3148
3420
  `;
3149
3421
  }
3150
3422
  async function cmdExport(target, projectRoot, options = {}) {
3151
- const essencePath = join14(projectRoot, "decantr.essence.json");
3152
- const tokensPath = join14(projectRoot, "src", "styles", "tokens.css");
3153
- if (!existsSync13(essencePath)) {
3423
+ const essencePath = join15(projectRoot, "decantr.essence.json");
3424
+ const tokensPath = join15(projectRoot, "src", "styles", "tokens.css");
3425
+ if (!existsSync14(essencePath)) {
3154
3426
  console.error(`${RED2}No decantr.essence.json found. Run \`decantr init\` first.${RESET3}`);
3155
3427
  process.exitCode = 1;
3156
3428
  return;
3157
3429
  }
3158
- if (!existsSync13(tokensPath)) {
3430
+ if (!existsSync14(tokensPath)) {
3159
3431
  console.error(
3160
3432
  `${RED2}No src/styles/tokens.css found. Run \`decantr refresh\` to generate tokens.${RESET3}`
3161
3433
  );
3162
3434
  process.exitCode = 1;
3163
3435
  return;
3164
3436
  }
3165
- const tokensCSS = readFileSync10(tokensPath, "utf-8");
3437
+ const tokensCSS = readFileSync11(tokensPath, "utf-8");
3166
3438
  const tokens = parseTokensCSS(tokensCSS);
3167
3439
  if (tokens.size === 0) {
3168
3440
  console.error(`${RED2}No --d-* tokens found in tokens.css.${RESET3}`);
@@ -3171,36 +3443,36 @@ async function cmdExport(target, projectRoot, options = {}) {
3171
3443
  }
3172
3444
  switch (target) {
3173
3445
  case "shadcn": {
3174
- const cssOut = options.output ?? join14(projectRoot, "src", "styles", "shadcn-theme.css");
3175
- const jsonOut = join14(projectRoot, "components.json");
3446
+ const cssOut = options.output ?? join15(projectRoot, "src", "styles", "shadcn-theme.css");
3447
+ const jsonOut = join15(projectRoot, "components.json");
3176
3448
  ensureDir(cssOut);
3177
- writeFileSync8(cssOut, generateShadcnCSS(tokens), "utf-8");
3178
- writeFileSync8(jsonOut, generateShadcnComponentsJSON(), "utf-8");
3449
+ writeFileSync9(cssOut, generateShadcnCSS(tokens), "utf-8");
3450
+ writeFileSync9(jsonOut, generateShadcnComponentsJSON(), "utf-8");
3179
3451
  console.log(`${GREEN3}Exported shadcn theme:${RESET3}`);
3180
3452
  console.log(` ${DIM3}CSS:${RESET3} ${cssOut}`);
3181
3453
  console.log(` ${DIM3}JSON:${RESET3} ${jsonOut}`);
3182
3454
  break;
3183
3455
  }
3184
3456
  case "tailwind": {
3185
- const out = options.output ?? join14(projectRoot, "tailwind.decantr.config.ts");
3457
+ const out = options.output ?? join15(projectRoot, "tailwind.decantr.config.ts");
3186
3458
  ensureDir(out);
3187
- writeFileSync8(out, generateTailwindConfig(tokens), "utf-8");
3459
+ writeFileSync9(out, generateTailwindConfig(tokens), "utf-8");
3188
3460
  console.log(`${GREEN3}Exported Tailwind config:${RESET3}`);
3189
3461
  console.log(` ${DIM3}File:${RESET3} ${out}`);
3190
3462
  break;
3191
3463
  }
3192
3464
  case "css-vars": {
3193
- const out = options.output ?? join14(projectRoot, "decantr-tokens.css");
3465
+ const out = options.output ?? join15(projectRoot, "decantr-tokens.css");
3194
3466
  ensureDir(out);
3195
- writeFileSync8(out, generateCSSVars(tokens), "utf-8");
3467
+ writeFileSync9(out, generateCSSVars(tokens), "utf-8");
3196
3468
  console.log(`${GREEN3}Exported CSS variables:${RESET3}`);
3197
3469
  console.log(` ${DIM3}File:${RESET3} ${out}`);
3198
3470
  break;
3199
3471
  }
3200
3472
  case "figma-tokens": {
3201
- const out = options.output ?? join14(projectRoot, ".decantr", "design", "figma-tokens.json");
3473
+ const out = options.output ?? join15(projectRoot, ".decantr", "design", "figma-tokens.json");
3202
3474
  ensureDir(out);
3203
- writeFileSync8(out, generateFigmaTokens(tokens), "utf-8");
3475
+ writeFileSync9(out, generateFigmaTokens(tokens), "utf-8");
3204
3476
  console.log(`${GREEN3}Exported Figma/Tokens Studio tokens:${RESET3}`);
3205
3477
  console.log(` ${DIM3}File:${RESET3} ${out}`);
3206
3478
  break;
@@ -3209,15 +3481,15 @@ async function cmdExport(target, projectRoot, options = {}) {
3209
3481
  }
3210
3482
  function ensureDir(filePath) {
3211
3483
  const dir = dirname2(filePath);
3212
- if (!existsSync13(dir)) {
3213
- mkdirSync7(dir, { recursive: true });
3484
+ if (!existsSync14(dir)) {
3485
+ mkdirSync8(dir, { recursive: true });
3214
3486
  }
3215
3487
  }
3216
3488
 
3217
3489
  // src/commands/magic.ts
3218
- import { existsSync as existsSync14 } from "fs";
3490
+ import { existsSync as existsSync15 } from "fs";
3219
3491
  import * as fs from "fs/promises";
3220
- import { join as join15 } from "path";
3492
+ import { join as join16 } from "path";
3221
3493
  var BOLD2 = "\x1B[1m";
3222
3494
  var DIM4 = "\x1B[2m";
3223
3495
  var RESET4 = "\x1B[0m";
@@ -3445,8 +3717,8 @@ async function cmdMagic(prompt, projectRoot, options) {
3445
3717
  console.log(` Archetype: ${intent.archetype}`);
3446
3718
  }
3447
3719
  console.log("");
3448
- const essencePath = join15(projectRoot, "decantr.essence.json");
3449
- if (existsSync14(essencePath)) {
3720
+ const essencePath = join16(projectRoot, "decantr.essence.json");
3721
+ if (existsSync15(essencePath)) {
3450
3722
  console.log(error(" decantr.essence.json already exists in this directory."));
3451
3723
  console.log(dim(" Remove it first or use a different directory."));
3452
3724
  process.exitCode = 1;
@@ -3471,7 +3743,7 @@ async function cmdMagic(prompt, projectRoot, options) {
3471
3743
  return;
3472
3744
  }
3473
3745
  const registryClient = new RegistryClient({
3474
- cacheDir: join15(projectRoot, ".decantr", "cache"),
3746
+ cacheDir: join16(projectRoot, ".decantr", "cache"),
3475
3747
  apiUrl: options.registry,
3476
3748
  offline: options.offline
3477
3749
  });
@@ -3742,14 +4014,14 @@ async function cmdMagic(prompt, projectRoot, options) {
3742
4014
  if (result.gitignoreUpdated) {
3743
4015
  console.log(` ${dim(".gitignore updated")}`);
3744
4016
  }
3745
- const contextDir = join15(projectRoot, ".decantr", "context");
4017
+ const contextDir = join16(projectRoot, ".decantr", "context");
3746
4018
  let sectionCount = 0;
3747
4019
  try {
3748
4020
  const files = await fs.readdir(contextDir);
3749
4021
  sectionCount = files.filter((f) => f.startsWith("section-")).length;
3750
4022
  } catch {
3751
4023
  }
3752
- const treatmentsPath = join15(projectRoot, "src", "styles", "treatments.css");
4024
+ const treatmentsPath = join16(projectRoot, "src", "styles", "treatments.css");
3753
4025
  let hasLayers = false;
3754
4026
  try {
3755
4027
  const css = await fs.readFile(treatmentsPath, "utf-8");
@@ -3782,8 +4054,8 @@ ${GREEN4}${BOLD2}Quality summary:${RESET4}`);
3782
4054
  }
3783
4055
 
3784
4056
  // src/commands/migrate.ts
3785
- import { copyFileSync, existsSync as existsSync15, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
3786
- import { join as join16 } from "path";
4057
+ import { copyFileSync, existsSync as existsSync16, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
4058
+ import { join as join17 } from "path";
3787
4059
  import {
3788
4060
  isV4 as isV43,
3789
4061
  migrateToV4,
@@ -3796,12 +4068,12 @@ var YELLOW3 = "\x1B[33m";
3796
4068
  var RESET5 = "\x1B[0m";
3797
4069
  var DIM5 = "\x1B[2m";
3798
4070
  function migrateEssenceFile(essencePath) {
3799
- if (!existsSync15(essencePath)) {
4071
+ if (!existsSync16(essencePath)) {
3800
4072
  return { success: false, error: `File not found: ${essencePath}` };
3801
4073
  }
3802
4074
  let raw;
3803
4075
  try {
3804
- raw = readFileSync11(essencePath, "utf-8");
4076
+ raw = readFileSync12(essencePath, "utf-8");
3805
4077
  } catch (e) {
3806
4078
  return { success: false, error: `Could not read ${essencePath}: ${e.message}` };
3807
4079
  }
@@ -3842,7 +4114,7 @@ function migrateEssenceFile(essencePath) {
3842
4114
  };
3843
4115
  }
3844
4116
  try {
3845
- writeFileSync9(essencePath, JSON.stringify(v4, null, 2) + "\n");
4117
+ writeFileSync10(essencePath, JSON.stringify(v4, null, 2) + "\n");
3846
4118
  } catch (e) {
3847
4119
  return {
3848
4120
  success: false,
@@ -3863,8 +4135,8 @@ async function cmdMigrate(projectRoot = process.cwd(), args = []) {
3863
4135
  process.exitCode = 1;
3864
4136
  return;
3865
4137
  }
3866
- const essencePath = join16(projectRoot, "decantr.essence.json");
3867
- if (!existsSync15(essencePath)) {
4138
+ const essencePath = join17(projectRoot, "decantr.essence.json");
4139
+ if (!existsSync16(essencePath)) {
3868
4140
  console.error(`${RED4}No decantr.essence.json found. Run \`decantr init\` first.${RESET5}`);
3869
4141
  process.exitCode = 1;
3870
4142
  return;
@@ -3886,7 +4158,7 @@ async function cmdMigrate(projectRoot = process.cwd(), args = []) {
3886
4158
  }
3887
4159
  if (result.essence) {
3888
4160
  const registryClient = new RegistryClient({
3889
- cacheDir: join16(projectRoot, ".decantr", "cache")
4161
+ cacheDir: join17(projectRoot, ".decantr", "cache")
3890
4162
  });
3891
4163
  await refreshDerivedFiles(projectRoot, result.essence, registryClient);
3892
4164
  console.log(`${GREEN5}Derived context and execution packs refreshed.${RESET5}`);
@@ -3897,50 +4169,50 @@ async function cmdMigrate(projectRoot = process.cwd(), args = []) {
3897
4169
 
3898
4170
  // src/commands/new-project.ts
3899
4171
  import { spawnSync } from "child_process";
3900
- import { existsSync as existsSync17, mkdirSync as mkdirSync9 } from "fs";
3901
- import { join as join18, resolve as resolve2 } from "path";
4172
+ import { existsSync as existsSync18, mkdirSync as mkdirSync10 } from "fs";
4173
+ import { join as join19, resolve as resolve2 } from "path";
3902
4174
  import { fileURLToPath } from "url";
3903
4175
 
3904
4176
  // src/offline-content.ts
3905
- import { cpSync, existsSync as existsSync16, mkdirSync as mkdirSync8 } from "fs";
3906
- import { join as join17, resolve } from "path";
4177
+ import { cpSync, existsSync as existsSync17, mkdirSync as mkdirSync9 } from "fs";
4178
+ import { join as join18, resolve } from "path";
3907
4179
  var CONTENT_TYPES2 = ["archetypes", "blueprints", "patterns", "themes", "shells"];
3908
4180
  function copyIfExists(source, target) {
3909
- if (!existsSync16(source)) return false;
4181
+ if (!existsSync17(source)) return false;
3910
4182
  if (resolve(source) === resolve(target)) return true;
3911
4183
  cpSync(source, target, { recursive: true });
3912
4184
  return true;
3913
4185
  }
3914
4186
  function hydrateContentRoot(projectDir, contentRoot) {
3915
- if (!existsSync16(contentRoot)) return false;
3916
- const customRoot = join17(projectDir, ".decantr", "custom");
3917
- const cacheRoot = join17(projectDir, ".decantr", "cache", "@official");
3918
- mkdirSync8(customRoot, { recursive: true });
3919
- mkdirSync8(cacheRoot, { recursive: true });
4187
+ if (!existsSync17(contentRoot)) return false;
4188
+ const customRoot = join18(projectDir, ".decantr", "custom");
4189
+ const cacheRoot = join18(projectDir, ".decantr", "cache", "@official");
4190
+ mkdirSync9(customRoot, { recursive: true });
4191
+ mkdirSync9(cacheRoot, { recursive: true });
3920
4192
  let copiedAny = false;
3921
4193
  for (const type of CONTENT_TYPES2) {
3922
- const sourceDir = join17(contentRoot, type);
3923
- if (!existsSync16(sourceDir)) continue;
3924
- cpSync(sourceDir, join17(customRoot, type), { recursive: true });
3925
- cpSync(sourceDir, join17(cacheRoot, type), { recursive: true });
4194
+ const sourceDir = join18(contentRoot, type);
4195
+ if (!existsSync17(sourceDir)) continue;
4196
+ cpSync(sourceDir, join18(customRoot, type), { recursive: true });
4197
+ cpSync(sourceDir, join18(cacheRoot, type), { recursive: true });
3926
4198
  copiedAny = true;
3927
4199
  }
3928
4200
  return copiedAny;
3929
4201
  }
3930
4202
  function seedOfflineRegistry(projectDir, workspaceRoot) {
3931
- const projectDecantrRoot = join17(projectDir, ".decantr");
3932
- mkdirSync8(projectDecantrRoot, { recursive: true });
4203
+ const projectDecantrRoot = join18(projectDir, ".decantr");
4204
+ mkdirSync9(projectDecantrRoot, { recursive: true });
3933
4205
  const configuredContentRoot = process.env.DECANTR_CONTENT_DIR ? resolve(process.env.DECANTR_CONTENT_DIR) : null;
3934
4206
  if (configuredContentRoot && hydrateContentRoot(projectDir, configuredContentRoot)) {
3935
4207
  return { seeded: true, strategy: "configured-content-root" };
3936
4208
  }
3937
4209
  const copiedCache = copyIfExists(
3938
- join17(workspaceRoot, ".decantr", "cache"),
3939
- join17(projectDecantrRoot, "cache")
4210
+ join18(workspaceRoot, ".decantr", "cache"),
4211
+ join18(projectDecantrRoot, "cache")
3940
4212
  );
3941
4213
  const copiedCustom = copyIfExists(
3942
- join17(workspaceRoot, ".decantr", "custom"),
3943
- join17(projectDecantrRoot, "custom")
4214
+ join18(workspaceRoot, ".decantr", "custom"),
4215
+ join18(projectDecantrRoot, "custom")
3944
4216
  );
3945
4217
  if (copiedCache || copiedCustom) {
3946
4218
  return { seeded: true, strategy: "workspace-cache" };
@@ -4053,7 +4325,7 @@ function runArgvCommand(command, args, cwd) {
4053
4325
  }
4054
4326
  function resolveInitCommand(initFlags) {
4055
4327
  const bundledCliEntrypoint = fileURLToPath(new URL("./bin.js", import.meta.url));
4056
- const cliEntrypoint = existsSync17(bundledCliEntrypoint) ? bundledCliEntrypoint : process.argv[1] && existsSync17(process.argv[1]) ? process.argv[1] : null;
4328
+ const cliEntrypoint = existsSync18(bundledCliEntrypoint) ? bundledCliEntrypoint : process.argv[1] && existsSync18(process.argv[1]) ? process.argv[1] : null;
4057
4329
  if (cliEntrypoint) {
4058
4330
  return {
4059
4331
  command: process.execPath,
@@ -4081,7 +4353,7 @@ async function cmdNewProject(projectName, options) {
4081
4353
  process.exitCode = 1;
4082
4354
  return;
4083
4355
  }
4084
- if (existsSync17(projectDir)) {
4356
+ if (existsSync18(projectDir)) {
4085
4357
  console.error(error2(`Directory "${projectName}" already exists.`));
4086
4358
  process.exitCode = 1;
4087
4359
  return;
@@ -4095,7 +4367,7 @@ async function cmdNewProject(projectName, options) {
4095
4367
  return;
4096
4368
  }
4097
4369
  console.log(heading(`Creating ${projectName}...`));
4098
- mkdirSync9(projectDir, { recursive: true });
4370
+ mkdirSync10(projectDir, { recursive: true });
4099
4371
  console.log(dim2(` Created ${projectName}/`));
4100
4372
  const title = projectName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
4101
4373
  if (shouldBootstrapRuntime && bootstrapAdapter) {
@@ -4194,21 +4466,21 @@ ${YELLOW4}Decantr init encountered issues. Run \`decantr init\` manually inside
4194
4466
  }
4195
4467
  }
4196
4468
  function detectPackageManager() {
4197
- if (existsSync17(join18(process.cwd(), "pnpm-lock.yaml")) || existsSync17(join18(process.cwd(), "pnpm-workspace.yaml"))) {
4469
+ if (existsSync18(join19(process.cwd(), "pnpm-lock.yaml")) || existsSync18(join19(process.cwd(), "pnpm-workspace.yaml"))) {
4198
4470
  return "pnpm";
4199
4471
  }
4200
- if (existsSync17(join18(process.cwd(), "yarn.lock"))) {
4472
+ if (existsSync18(join19(process.cwd(), "yarn.lock"))) {
4201
4473
  return "yarn";
4202
4474
  }
4203
- if (existsSync17(join18(process.cwd(), "bun.lockb")) || existsSync17(join18(process.cwd(), "bun.lock"))) {
4475
+ if (existsSync18(join19(process.cwd(), "bun.lockb")) || existsSync18(join19(process.cwd(), "bun.lock"))) {
4204
4476
  return "bun";
4205
4477
  }
4206
4478
  return "npm";
4207
4479
  }
4208
4480
 
4209
4481
  // src/commands/publish.ts
4210
- import { existsSync as existsSync18, readFileSync as readFileSync12 } from "fs";
4211
- import { join as join19 } from "path";
4482
+ import { existsSync as existsSync19, readFileSync as readFileSync13 } from "fs";
4483
+ import { join as join20 } from "path";
4212
4484
  import {
4213
4485
  API_CONTENT_TYPE_TO_CONTENT_TYPE,
4214
4486
  CONTENT_TYPE_TO_API_CONTENT_TYPE as CONTENT_TYPE_TO_API_CONTENT_TYPE2,
@@ -4224,8 +4496,8 @@ async function cmdPublish(type, name, projectRoot = process.cwd()) {
4224
4496
  }
4225
4497
  const singularType = API_CONTENT_TYPE_TO_CONTENT_TYPE[type] || type;
4226
4498
  const pluralType = CONTENT_TYPE_TO_API_CONTENT_TYPE2[type] || CONTENT_TYPE_TO_API_CONTENT_TYPE2[singularType] || `${type}s`;
4227
- const customPath = join19(projectRoot, ".decantr", "custom", pluralType, `${name}.json`);
4228
- if (!existsSync18(customPath)) {
4499
+ const customPath = join20(projectRoot, ".decantr", "custom", pluralType, `${name}.json`);
4500
+ if (!existsSync19(customPath)) {
4229
4501
  console.error(`Custom ${singularType} "${name}" not found at ${customPath}`);
4230
4502
  console.error(`Create one first: decantr create ${singularType} ${name}`);
4231
4503
  process.exitCode = 1;
@@ -4233,7 +4505,7 @@ async function cmdPublish(type, name, projectRoot = process.cwd()) {
4233
4505
  }
4234
4506
  let data;
4235
4507
  try {
4236
- data = JSON.parse(readFileSync12(customPath, "utf-8"));
4508
+ data = JSON.parse(readFileSync13(customPath, "utf-8"));
4237
4509
  } catch {
4238
4510
  console.error(`Failed to parse ${customPath}`);
4239
4511
  process.exitCode = 1;
@@ -4271,23 +4543,23 @@ async function cmdPublish(type, name, projectRoot = process.cwd()) {
4271
4543
  }
4272
4544
 
4273
4545
  // src/commands/refresh.ts
4274
- import { existsSync as existsSync19, readFileSync as readFileSync13 } from "fs";
4275
- import { join as join20 } from "path";
4546
+ import { existsSync as existsSync20, readFileSync as readFileSync14 } from "fs";
4547
+ import { join as join21 } from "path";
4276
4548
  import { isV4 as isV44 } from "@decantr/essence-spec";
4277
4549
  var GREEN7 = "\x1B[32m";
4278
4550
  var RED6 = "\x1B[31m";
4279
4551
  var DIM7 = "\x1B[2m";
4280
4552
  var RESET7 = "\x1B[0m";
4281
4553
  async function cmdRefresh(projectRoot = process.cwd(), options = {}) {
4282
- const essencePath = join20(projectRoot, "decantr.essence.json");
4283
- if (!existsSync19(essencePath)) {
4554
+ const essencePath = join21(projectRoot, "decantr.essence.json");
4555
+ if (!existsSync20(essencePath)) {
4284
4556
  console.error(`${RED6}No decantr.essence.json found. Run \`decantr init\` first.${RESET7}`);
4285
4557
  process.exitCode = 1;
4286
4558
  return;
4287
4559
  }
4288
4560
  let essence;
4289
4561
  try {
4290
- const raw = readFileSync13(essencePath, "utf-8");
4562
+ const raw = readFileSync14(essencePath, "utf-8");
4291
4563
  const parsed = JSON.parse(raw);
4292
4564
  if (!isV44(parsed)) {
4293
4565
  console.error(
@@ -4303,7 +4575,7 @@ async function cmdRefresh(projectRoot = process.cwd(), options = {}) {
4303
4575
  return;
4304
4576
  }
4305
4577
  const registryClient = new RegistryClient({
4306
- cacheDir: join20(projectRoot, ".decantr", "cache"),
4578
+ cacheDir: join21(projectRoot, ".decantr", "cache"),
4307
4579
  offline: options.offline
4308
4580
  });
4309
4581
  console.log("Regenerating derived files...\n");
@@ -4323,8 +4595,8 @@ async function cmdRefresh(projectRoot = process.cwd(), options = {}) {
4323
4595
  }
4324
4596
 
4325
4597
  // src/commands/registry-mirror.ts
4326
- import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
4327
- import { join as join21 } from "path";
4598
+ import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
4599
+ import { join as join22 } from "path";
4328
4600
  import { API_CONTENT_TYPES, RegistryAPIClient as RegistryAPIClient2 } from "@decantr/registry";
4329
4601
  var GREEN8 = "\x1B[32m";
4330
4602
  var RED7 = "\x1B[31m";
@@ -4352,7 +4624,7 @@ async function cmdRegistryMirror(projectRoot, options = {}) {
4352
4624
  process.exitCode = 1;
4353
4625
  return;
4354
4626
  }
4355
- const cacheDir = join21(projectRoot, ".decantr", "cache");
4627
+ const cacheDir = join22(projectRoot, ".decantr", "cache");
4356
4628
  const counts = {};
4357
4629
  const failed = [];
4358
4630
  console.log(`
@@ -4362,19 +4634,19 @@ Mirroring registry content to ${DIM8}.decantr/cache/${RESET8}
4362
4634
  try {
4363
4635
  const result = await apiClient.listContent(type, { namespace: "@official" });
4364
4636
  const items = result.items;
4365
- const typeDir = join21(cacheDir, "@official", type);
4366
- mkdirSync10(typeDir, { recursive: true });
4367
- writeFileSync10(join21(typeDir, "index.json"), JSON.stringify(result, null, 2));
4637
+ const typeDir = join22(cacheDir, "@official", type);
4638
+ mkdirSync11(typeDir, { recursive: true });
4639
+ writeFileSync11(join22(typeDir, "index.json"), JSON.stringify(result, null, 2));
4368
4640
  let itemCount = 0;
4369
4641
  for (const item of items) {
4370
4642
  const slug = item.slug || item.id;
4371
4643
  if (!slug) continue;
4372
4644
  try {
4373
4645
  const fullItem = await apiClient.getContent(type, "@official", slug);
4374
- writeFileSync10(join21(typeDir, `${slug}.json`), JSON.stringify(fullItem, null, 2));
4646
+ writeFileSync11(join22(typeDir, `${slug}.json`), JSON.stringify(fullItem, null, 2));
4375
4647
  itemCount++;
4376
4648
  } catch {
4377
- writeFileSync10(join21(typeDir, `${slug}.json`), JSON.stringify(item, null, 2));
4649
+ writeFileSync11(join22(typeDir, `${slug}.json`), JSON.stringify(item, null, 2));
4378
4650
  itemCount++;
4379
4651
  }
4380
4652
  }
@@ -4389,8 +4661,8 @@ Mirroring registry content to ${DIM8}.decantr/cache/${RESET8}
4389
4661
  mirrored_at: (/* @__PURE__ */ new Date()).toISOString(),
4390
4662
  counts
4391
4663
  };
4392
- mkdirSync10(join21(cacheDir), { recursive: true });
4393
- writeFileSync10(join21(cacheDir, "mirror-manifest.json"), JSON.stringify(manifest, null, 2));
4664
+ mkdirSync11(join22(cacheDir), { recursive: true });
4665
+ writeFileSync11(join22(cacheDir, "mirror-manifest.json"), JSON.stringify(manifest, null, 2));
4394
4666
  const totalItems = Object.values(counts).reduce((a, b) => a + b, 0);
4395
4667
  console.log("");
4396
4668
  if (failed.length > 0) {
@@ -4407,23 +4679,23 @@ Mirroring registry content to ${DIM8}.decantr/cache/${RESET8}
4407
4679
  }
4408
4680
 
4409
4681
  // src/commands/remove.ts
4410
- import { existsSync as existsSync21, readFileSync as readFileSync14, rmSync as rmSync2, writeFileSync as writeFileSync11 } from "fs";
4411
- import { join as join22 } from "path";
4682
+ import { existsSync as existsSync22, readFileSync as readFileSync15, rmSync as rmSync2, writeFileSync as writeFileSync12 } from "fs";
4683
+ import { join as join23 } from "path";
4412
4684
  import { isV4 as isV45 } from "@decantr/essence-spec";
4413
4685
  var GREEN9 = "\x1B[32m";
4414
4686
  var RED8 = "\x1B[31m";
4415
4687
  var DIM9 = "\x1B[2m";
4416
4688
  var RESET9 = "\x1B[0m";
4417
4689
  function readV4Essence2(projectRoot) {
4418
- const essencePath = join22(projectRoot, "decantr.essence.json");
4419
- if (!existsSync21(essencePath)) {
4690
+ const essencePath = join23(projectRoot, "decantr.essence.json");
4691
+ if (!existsSync22(essencePath)) {
4420
4692
  console.error(`${RED8}No decantr.essence.json found. Run \`decantr init\` first.${RESET9}`);
4421
4693
  process.exitCode = 1;
4422
4694
  return null;
4423
4695
  }
4424
4696
  let parsed;
4425
4697
  try {
4426
- parsed = JSON.parse(readFileSync14(essencePath, "utf-8"));
4698
+ parsed = JSON.parse(readFileSync15(essencePath, "utf-8"));
4427
4699
  } catch (e) {
4428
4700
  console.error(`${RED8}Could not read essence: ${e.message}${RESET9}`);
4429
4701
  process.exitCode = 1;
@@ -4439,7 +4711,7 @@ function readV4Essence2(projectRoot) {
4439
4711
  return { essence: parsed, essencePath };
4440
4712
  }
4441
4713
  function writeEssence2(essencePath, essence) {
4442
- writeFileSync11(essencePath, JSON.stringify(essence, null, 2) + "\n");
4714
+ writeFileSync12(essencePath, JSON.stringify(essence, null, 2) + "\n");
4443
4715
  }
4444
4716
  function recomputeGlobalFeatures(essence) {
4445
4717
  const all = /* @__PURE__ */ new Set();
@@ -4481,14 +4753,14 @@ async function cmdRemoveSection(sectionId, args, projectRoot = process.cwd()) {
4481
4753
  sections.splice(idx, 1);
4482
4754
  recomputeGlobalFeatures(essence);
4483
4755
  removeRoutes(essence, sectionId);
4484
- const contextFile = join22(projectRoot, ".decantr", "context", `${sectionId}.md`);
4485
- if (existsSync21(contextFile)) {
4756
+ const contextFile = join23(projectRoot, ".decantr", "context", `${sectionId}.md`);
4757
+ if (existsSync22(contextFile)) {
4486
4758
  rmSync2(contextFile);
4487
4759
  }
4488
4760
  writeEssence2(essencePath, essence);
4489
4761
  console.log(`${GREEN9}Removed section "${sectionId}".${RESET9}`);
4490
4762
  const registryClient = new RegistryClient({
4491
- cacheDir: join22(projectRoot, ".decantr", "cache")
4763
+ cacheDir: join23(projectRoot, ".decantr", "cache")
4492
4764
  });
4493
4765
  await refreshDerivedFiles(projectRoot, essence, registryClient);
4494
4766
  console.log(`${GREEN9}Derived files refreshed.${RESET9}`);
@@ -4524,7 +4796,7 @@ async function cmdRemovePage(path, args, projectRoot = process.cwd()) {
4524
4796
  writeEssence2(essencePath, essence);
4525
4797
  console.log(`${GREEN9}Removed page "${pageId}" from section "${sectionId}".${RESET9}`);
4526
4798
  const registryClient = new RegistryClient({
4527
- cacheDir: join22(projectRoot, ".decantr", "cache")
4799
+ cacheDir: join23(projectRoot, ".decantr", "cache")
4528
4800
  });
4529
4801
  await refreshDerivedFiles(projectRoot, essence, registryClient);
4530
4802
  console.log(`${GREEN9}Derived files refreshed.${RESET9}`);
@@ -4565,15 +4837,15 @@ async function cmdRemoveFeature(feature, args, projectRoot = process.cwd()) {
4565
4837
  const target = sectionId ? `section "${sectionId}" and global` : "global";
4566
4838
  console.log(`${GREEN9}Removed feature "${feature}" from ${target} features.${RESET9}`);
4567
4839
  const registryClient = new RegistryClient({
4568
- cacheDir: join22(projectRoot, ".decantr", "cache")
4840
+ cacheDir: join23(projectRoot, ".decantr", "cache")
4569
4841
  });
4570
4842
  await refreshDerivedFiles(projectRoot, essence, registryClient);
4571
4843
  console.log(`${GREEN9}Derived files refreshed.${RESET9}`);
4572
4844
  }
4573
4845
 
4574
4846
  // src/commands/sync-drift.ts
4575
- import { existsSync as existsSync22, readFileSync as readFileSync15, writeFileSync as writeFileSync12 } from "fs";
4576
- import { join as join23 } from "path";
4847
+ import { existsSync as existsSync23, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "fs";
4848
+ import { join as join24 } from "path";
4577
4849
  var GREEN10 = "\x1B[32m";
4578
4850
  var RED9 = "\x1B[31m";
4579
4851
  var YELLOW6 = "\x1B[33m";
@@ -4582,8 +4854,8 @@ var DIM10 = "\x1B[2m";
4582
4854
  var BOLD4 = "\x1B[1m";
4583
4855
  var CYAN5 = "\x1B[36m";
4584
4856
  async function cmdSyncDrift(projectRoot = process.cwd()) {
4585
- const driftLogPath = join23(projectRoot, ".decantr", "drift-log.json");
4586
- if (!existsSync22(driftLogPath)) {
4857
+ const driftLogPath = join24(projectRoot, ".decantr", "drift-log.json");
4858
+ if (!existsSync23(driftLogPath)) {
4587
4859
  console.log(`${GREEN10}No drift log found \u2014 no drift recorded.${RESET10}`);
4588
4860
  console.log(
4589
4861
  `${DIM10}Drift is logged when guard violations are detected during development.${RESET10}`
@@ -4592,7 +4864,7 @@ async function cmdSyncDrift(projectRoot = process.cwd()) {
4592
4864
  }
4593
4865
  let entries;
4594
4866
  try {
4595
- entries = JSON.parse(readFileSync15(driftLogPath, "utf-8"));
4867
+ entries = JSON.parse(readFileSync16(driftLogPath, "utf-8"));
4596
4868
  } catch {
4597
4869
  console.error(`${RED9}Could not parse drift-log.json${RESET10}`);
4598
4870
  process.exitCode = 1;
@@ -4638,13 +4910,13 @@ ${BOLD4}Unresolved Drift Entries (${unresolved.length})${RESET10}
4638
4910
  console.log("");
4639
4911
  }
4640
4912
  function resolveDriftEntries(projectRoot, options) {
4641
- const driftLogPath = join23(projectRoot, ".decantr", "drift-log.json");
4642
- if (!existsSync22(driftLogPath)) {
4913
+ const driftLogPath = join24(projectRoot, ".decantr", "drift-log.json");
4914
+ if (!existsSync23(driftLogPath)) {
4643
4915
  return { success: true };
4644
4916
  }
4645
4917
  if (options.clear) {
4646
4918
  try {
4647
- writeFileSync12(driftLogPath, "[]");
4919
+ writeFileSync13(driftLogPath, "[]");
4648
4920
  return { success: true };
4649
4921
  } catch (e) {
4650
4922
  return { success: false, error: `Could not clear drift log: ${e.message}` };
@@ -4652,7 +4924,7 @@ function resolveDriftEntries(projectRoot, options) {
4652
4924
  }
4653
4925
  let entries;
4654
4926
  try {
4655
- entries = JSON.parse(readFileSync15(driftLogPath, "utf-8"));
4927
+ entries = JSON.parse(readFileSync16(driftLogPath, "utf-8"));
4656
4928
  } catch {
4657
4929
  return { success: false, error: "Could not parse drift-log.json" };
4658
4930
  }
@@ -4671,7 +4943,7 @@ function resolveDriftEntries(projectRoot, options) {
4671
4943
  }
4672
4944
  }
4673
4945
  try {
4674
- writeFileSync12(driftLogPath, JSON.stringify(entries, null, 2));
4946
+ writeFileSync13(driftLogPath, JSON.stringify(entries, null, 2));
4675
4947
  return { success: true };
4676
4948
  } catch (e) {
4677
4949
  return { success: false, error: `Could not write drift log: ${e.message}` };
@@ -4896,8 +5168,8 @@ function trimTrailingSlashes(value) {
4896
5168
  }
4897
5169
 
4898
5170
  // src/commands/theme-switch.ts
4899
- import { existsSync as existsSync23, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "fs";
4900
- import { join as join24 } from "path";
5171
+ import { existsSync as existsSync24, readFileSync as readFileSync17, writeFileSync as writeFileSync14 } from "fs";
5172
+ import { join as join25 } from "path";
4901
5173
  import { isV4 as isV46 } from "@decantr/essence-spec";
4902
5174
  var GREEN12 = "\x1B[32m";
4903
5175
  var RED10 = "\x1B[31m";
@@ -4914,15 +5186,15 @@ async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
4914
5186
  process.exitCode = 1;
4915
5187
  return;
4916
5188
  }
4917
- const essencePath = join24(projectRoot, "decantr.essence.json");
4918
- if (!existsSync23(essencePath)) {
5189
+ const essencePath = join25(projectRoot, "decantr.essence.json");
5190
+ if (!existsSync24(essencePath)) {
4919
5191
  console.error(`${RED10}No decantr.essence.json found. Run \`decantr init\` first.${RESET12}`);
4920
5192
  process.exitCode = 1;
4921
5193
  return;
4922
5194
  }
4923
5195
  let parsed;
4924
5196
  try {
4925
- parsed = JSON.parse(readFileSync16(essencePath, "utf-8"));
5197
+ parsed = JSON.parse(readFileSync17(essencePath, "utf-8"));
4926
5198
  } catch (e) {
4927
5199
  console.error(`${RED10}Could not read essence: ${e.message}${RESET12}`);
4928
5200
  process.exitCode = 1;
@@ -4973,7 +5245,7 @@ async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
4973
5245
  essence.dna.theme.mode = mode;
4974
5246
  }
4975
5247
  const registryClient = new RegistryClient({
4976
- cacheDir: join24(projectRoot, ".decantr", "cache")
5248
+ cacheDir: join25(projectRoot, ".decantr", "cache")
4977
5249
  });
4978
5250
  try {
4979
5251
  const themeResult = await registryClient.fetchTheme(themeName);
@@ -4988,7 +5260,7 @@ async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
4988
5260
  }
4989
5261
  } catch {
4990
5262
  }
4991
- writeFileSync13(essencePath, JSON.stringify(essence, null, 2) + "\n");
5263
+ writeFileSync14(essencePath, JSON.stringify(essence, null, 2) + "\n");
4992
5264
  console.log(`${GREEN12}Switched theme: ${oldThemeId} \u2192 ${themeName}${RESET12}`);
4993
5265
  if (shape) console.log(` ${DIM12}Shape: ${shape}${RESET12}`);
4994
5266
  if (mode) console.log(` ${DIM12}Mode: ${mode}${RESET12}`);
@@ -5296,8 +5568,8 @@ async function runSimplifiedInit(blueprints) {
5296
5568
  }
5297
5569
 
5298
5570
  // src/theme-commands.ts
5299
- import { existsSync as existsSync24, mkdirSync as mkdirSync11, readdirSync as readdirSync4, readFileSync as readFileSync17, rmSync as rmSync3, writeFileSync as writeFileSync14 } from "fs";
5300
- import { join as join25 } from "path";
5571
+ import { existsSync as existsSync25, mkdirSync as mkdirSync12, readdirSync as readdirSync5, readFileSync as readFileSync18, rmSync as rmSync3, writeFileSync as writeFileSync15 } from "fs";
5572
+ import { join as join26 } from "path";
5301
5573
  var REQUIRED_FIELDS = [
5302
5574
  "$schema",
5303
5575
  "id",
@@ -5357,20 +5629,20 @@ function validateCustomTheme(theme) {
5357
5629
  };
5358
5630
  }
5359
5631
  function createTheme(projectRoot, id, name) {
5360
- const customThemesDir = join25(projectRoot, ".decantr", "custom", "themes");
5361
- const themePath = join25(customThemesDir, `${id}.json`);
5362
- const howToPath = join25(customThemesDir, "how-to-theme.md");
5363
- mkdirSync11(customThemesDir, { recursive: true });
5364
- if (existsSync24(themePath)) {
5632
+ const customThemesDir = join26(projectRoot, ".decantr", "custom", "themes");
5633
+ const themePath = join26(customThemesDir, `${id}.json`);
5634
+ const howToPath = join26(customThemesDir, "how-to-theme.md");
5635
+ mkdirSync12(customThemesDir, { recursive: true });
5636
+ if (existsSync25(themePath)) {
5365
5637
  return {
5366
5638
  success: false,
5367
5639
  error: `Theme "${id}" already exists at ${themePath}`
5368
5640
  };
5369
5641
  }
5370
5642
  const skeleton = getThemeSkeleton(id, name);
5371
- writeFileSync14(themePath, JSON.stringify(skeleton, null, 2));
5372
- if (!existsSync24(howToPath)) {
5373
- writeFileSync14(howToPath, getHowToThemeDoc());
5643
+ writeFileSync15(themePath, JSON.stringify(skeleton, null, 2));
5644
+ if (!existsSync25(howToPath)) {
5645
+ writeFileSync15(howToPath, getHowToThemeDoc());
5374
5646
  }
5375
5647
  return {
5376
5648
  success: true,
@@ -5378,17 +5650,17 @@ function createTheme(projectRoot, id, name) {
5378
5650
  };
5379
5651
  }
5380
5652
  function listCustomThemes(projectRoot) {
5381
- const customThemesDir = join25(projectRoot, ".decantr", "custom", "themes");
5382
- if (!existsSync24(customThemesDir)) {
5653
+ const customThemesDir = join26(projectRoot, ".decantr", "custom", "themes");
5654
+ if (!existsSync25(customThemesDir)) {
5383
5655
  return [];
5384
5656
  }
5385
5657
  const themes = [];
5386
5658
  try {
5387
- const files = readdirSync4(customThemesDir).filter((f) => f.endsWith(".json"));
5659
+ const files = readdirSync5(customThemesDir).filter((f) => f.endsWith(".json"));
5388
5660
  for (const file of files) {
5389
- const filePath = join25(customThemesDir, file);
5661
+ const filePath = join26(customThemesDir, file);
5390
5662
  try {
5391
- const data = JSON.parse(readFileSync17(filePath, "utf-8"));
5663
+ const data = JSON.parse(readFileSync18(filePath, "utf-8"));
5392
5664
  themes.push({
5393
5665
  id: data.id || file.replace(".json", ""),
5394
5666
  name: data.name || data.id,
@@ -5403,8 +5675,8 @@ function listCustomThemes(projectRoot) {
5403
5675
  return themes;
5404
5676
  }
5405
5677
  function deleteTheme(projectRoot, id) {
5406
- const themePath = join25(projectRoot, ".decantr", "custom", "themes", `${id}.json`);
5407
- if (!existsSync24(themePath)) {
5678
+ const themePath = join26(projectRoot, ".decantr", "custom", "themes", `${id}.json`);
5679
+ if (!existsSync25(themePath)) {
5408
5680
  return {
5409
5681
  success: false,
5410
5682
  error: `Theme "${id}" not found at ${themePath}`
@@ -5421,7 +5693,7 @@ function deleteTheme(projectRoot, id) {
5421
5693
  }
5422
5694
  }
5423
5695
  function importTheme(projectRoot, sourcePath) {
5424
- if (!existsSync24(sourcePath)) {
5696
+ if (!existsSync25(sourcePath)) {
5425
5697
  return {
5426
5698
  success: false,
5427
5699
  errors: [`Source file not found: ${sourcePath}`]
@@ -5429,7 +5701,7 @@ function importTheme(projectRoot, sourcePath) {
5429
5701
  }
5430
5702
  let theme;
5431
5703
  try {
5432
- theme = JSON.parse(readFileSync17(sourcePath, "utf-8"));
5704
+ theme = JSON.parse(readFileSync18(sourcePath, "utf-8"));
5433
5705
  } catch (e) {
5434
5706
  return {
5435
5707
  success: false,
@@ -5445,14 +5717,14 @@ function importTheme(projectRoot, sourcePath) {
5445
5717
  }
5446
5718
  theme.source = "custom";
5447
5719
  const id = theme.id;
5448
- const customThemesDir = join25(projectRoot, ".decantr", "custom", "themes");
5449
- const destPath = join25(customThemesDir, `${id}.json`);
5450
- mkdirSync11(customThemesDir, { recursive: true });
5451
- const howToPath = join25(customThemesDir, "how-to-theme.md");
5452
- if (!existsSync24(howToPath)) {
5453
- writeFileSync14(howToPath, getHowToThemeDoc());
5454
- }
5455
- writeFileSync14(destPath, JSON.stringify(theme, null, 2));
5720
+ const customThemesDir = join26(projectRoot, ".decantr", "custom", "themes");
5721
+ const destPath = join26(customThemesDir, `${id}.json`);
5722
+ mkdirSync12(customThemesDir, { recursive: true });
5723
+ const howToPath = join26(customThemesDir, "how-to-theme.md");
5724
+ if (!existsSync25(howToPath)) {
5725
+ writeFileSync15(howToPath, getHowToThemeDoc());
5726
+ }
5727
+ writeFileSync15(destPath, JSON.stringify(theme, null, 2));
5456
5728
  return {
5457
5729
  success: true,
5458
5730
  path: destPath
@@ -5460,19 +5732,19 @@ function importTheme(projectRoot, sourcePath) {
5460
5732
  }
5461
5733
 
5462
5734
  // src/workspace.ts
5463
- import { existsSync as existsSync25, readdirSync as readdirSync5, readFileSync as readFileSync18 } from "fs";
5464
- import { dirname as dirname3, join as join26, resolve as resolve3 } from "path";
5735
+ import { existsSync as existsSync26, readdirSync as readdirSync6, readFileSync as readFileSync19 } from "fs";
5736
+ import { dirname as dirname3, join as join27, resolve as resolve3 } from "path";
5465
5737
  function readPackageJson(dir) {
5466
- const path = join26(dir, "package.json");
5467
- if (!existsSync25(path)) return null;
5738
+ const path = join27(dir, "package.json");
5739
+ if (!existsSync26(path)) return null;
5468
5740
  try {
5469
- return JSON.parse(readFileSync18(path, "utf-8"));
5741
+ return JSON.parse(readFileSync19(path, "utf-8"));
5470
5742
  } catch {
5471
5743
  return null;
5472
5744
  }
5473
5745
  }
5474
5746
  function hasWorkspaceMarker(dir) {
5475
- if (existsSync25(join26(dir, "pnpm-workspace.yaml")) || existsSync25(join26(dir, "turbo.json")) || existsSync25(join26(dir, "nx.json"))) {
5747
+ if (existsSync26(join27(dir, "pnpm-workspace.yaml")) || existsSync26(join27(dir, "turbo.json")) || existsSync26(join27(dir, "nx.json"))) {
5476
5748
  return true;
5477
5749
  }
5478
5750
  const pkg = readPackageJson(dir);
@@ -5488,7 +5760,7 @@ function findWorkspaceRoot(startDir) {
5488
5760
  }
5489
5761
  }
5490
5762
  function looksLikeApp(dir) {
5491
- if (existsSync25(join26(dir, "next.config.js")) || existsSync25(join26(dir, "next.config.ts")) || existsSync25(join26(dir, "next.config.mjs")) || existsSync25(join26(dir, "vite.config.ts")) || existsSync25(join26(dir, "vite.config.js")) || existsSync25(join26(dir, "angular.json")) || existsSync25(join26(dir, "svelte.config.js")) || existsSync25(join26(dir, "svelte.config.ts")) || existsSync25(join26(dir, "astro.config.mjs")) || existsSync25(join26(dir, "src")) || existsSync25(join26(dir, "app")) || existsSync25(join26(dir, "pages"))) {
5763
+ if (existsSync26(join27(dir, "next.config.js")) || existsSync26(join27(dir, "next.config.ts")) || existsSync26(join27(dir, "next.config.mjs")) || existsSync26(join27(dir, "vite.config.ts")) || existsSync26(join27(dir, "vite.config.js")) || existsSync26(join27(dir, "angular.json")) || existsSync26(join27(dir, "svelte.config.js")) || existsSync26(join27(dir, "svelte.config.ts")) || existsSync26(join27(dir, "astro.config.mjs")) || existsSync26(join27(dir, "src")) || existsSync26(join27(dir, "app")) || existsSync26(join27(dir, "pages"))) {
5492
5764
  return true;
5493
5765
  }
5494
5766
  const pkg = readPackageJson(dir);
@@ -5500,11 +5772,11 @@ function looksLikeApp(dir) {
5500
5772
  function listWorkspaceApps(workspaceRoot) {
5501
5773
  const candidates = [];
5502
5774
  for (const base of ["apps", "packages"]) {
5503
- const baseDir = join26(workspaceRoot, base);
5504
- if (!existsSync25(baseDir)) continue;
5505
- for (const entry of readdirSync5(baseDir, { withFileTypes: true })) {
5775
+ const baseDir = join27(workspaceRoot, base);
5776
+ if (!existsSync26(baseDir)) continue;
5777
+ for (const entry of readdirSync6(baseDir, { withFileTypes: true })) {
5506
5778
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
5507
- const candidate = join26(baseDir, entry.name);
5779
+ const candidate = join27(baseDir, entry.name);
5508
5780
  if (looksLikeApp(candidate)) {
5509
5781
  candidates.push(`${base}/${entry.name}`);
5510
5782
  }
@@ -6069,18 +6341,18 @@ function extractHostedAssetPaths(indexHtml) {
6069
6341
  return [...assetPaths];
6070
6342
  }
6071
6343
  function readHostedDistSnapshot(distPath) {
6072
- const resolvedDistPath = distPath ? resolveUserPath(distPath) : join27(process.cwd(), "dist");
6073
- const indexPath = join27(resolvedDistPath, "index.html");
6074
- if (!existsSync26(indexPath)) {
6344
+ const resolvedDistPath = distPath ? resolveUserPath(distPath) : join28(process.cwd(), "dist");
6345
+ const indexPath = join28(resolvedDistPath, "index.html");
6346
+ if (!existsSync27(indexPath)) {
6075
6347
  return void 0;
6076
6348
  }
6077
- const indexHtml = readFileSync19(indexPath, "utf-8");
6349
+ const indexHtml = readFileSync20(indexPath, "utf-8");
6078
6350
  const assetPaths = extractHostedAssetPaths(indexHtml);
6079
6351
  const assets = {};
6080
6352
  for (const assetPath of assetPaths) {
6081
- const assetFilePath = join27(resolvedDistPath, assetPath.replace(/^[/\\]+/, ""));
6082
- if (existsSync26(assetFilePath)) {
6083
- assets[assetPath] = readFileSync19(assetFilePath, "utf-8");
6353
+ const assetFilePath = join28(resolvedDistPath, assetPath.replace(/^[/\\]+/, ""));
6354
+ if (existsSync27(assetFilePath)) {
6355
+ assets[assetPath] = readFileSync20(assetFilePath, "utf-8");
6084
6356
  }
6085
6357
  }
6086
6358
  return {
@@ -6095,7 +6367,7 @@ function isHostedSourceSnapshotFile(path) {
6095
6367
  function readHostedSourceSnapshot(sourcePath) {
6096
6368
  if (!sourcePath) return void 0;
6097
6369
  const resolvedSourcePath = resolveUserPath(sourcePath);
6098
- if (!existsSync26(resolvedSourcePath)) {
6370
+ if (!existsSync27(resolvedSourcePath)) {
6099
6371
  return void 0;
6100
6372
  }
6101
6373
  const files = {};
@@ -6109,17 +6381,17 @@ function readHostedSourceSnapshot(sourcePath) {
6109
6381
  ]);
6110
6382
  const rootPrefix = basename2(resolvedSourcePath);
6111
6383
  const walk = (absoluteDir, relativeDir) => {
6112
- for (const entry of readdirSync6(absoluteDir, { withFileTypes: true })) {
6384
+ for (const entry of readdirSync7(absoluteDir, { withFileTypes: true })) {
6113
6385
  if (ignoredDirNames.has(entry.name)) continue;
6114
- const absolutePath = join27(absoluteDir, entry.name);
6115
- const relativePath = join27(relativeDir, entry.name).replace(/\\/g, "/");
6386
+ const absolutePath = join28(absoluteDir, entry.name);
6387
+ const relativePath = join28(relativeDir, entry.name).replace(/\\/g, "/");
6116
6388
  if (entry.isDirectory()) {
6117
6389
  walk(absolutePath, relativePath);
6118
6390
  continue;
6119
6391
  }
6120
6392
  if (!entry.isFile()) continue;
6121
6393
  if (!isHostedSourceSnapshotFile(relativePath)) continue;
6122
- files[relativePath] = readFileSync19(absolutePath, "utf-8");
6394
+ files[relativePath] = readFileSync20(absolutePath, "utf-8");
6123
6395
  }
6124
6396
  };
6125
6397
  walk(resolvedSourcePath, rootPrefix);
@@ -6270,16 +6542,16 @@ async function printRegistryIntelligenceSummary(namespace, jsonOutput = false) {
6270
6542
  }
6271
6543
  async function printHostedExecutionPackBundle(essencePath, namespace, jsonOutput = false, writeContext = false) {
6272
6544
  const client = getPublicAPIClient();
6273
- const resolvedPath = essencePath ? resolveUserPath(essencePath) : join27(process.cwd(), "decantr.essence.json");
6274
- if (!existsSync26(resolvedPath)) {
6545
+ const resolvedPath = essencePath ? resolveUserPath(essencePath) : join28(process.cwd(), "decantr.essence.json");
6546
+ if (!existsSync27(resolvedPath)) {
6275
6547
  throw new Error(`Essence file not found at ${resolvedPath}`);
6276
6548
  }
6277
- const essence = JSON.parse(readFileSync19(resolvedPath, "utf-8"));
6549
+ const essence = JSON.parse(readFileSync20(resolvedPath, "utf-8"));
6278
6550
  const bundle = await client.compileExecutionPacks(essence, namespace ? { namespace } : void 0);
6279
6551
  let writtenContextPaths = [];
6280
6552
  if (writeContext) {
6281
- const contextDir = join27(process.cwd(), ".decantr", "context");
6282
- mkdirSync12(contextDir, { recursive: true });
6553
+ const contextDir = join28(process.cwd(), ".decantr", "context");
6554
+ mkdirSync13(contextDir, { recursive: true });
6283
6555
  const written = writeExecutionPackBundleArtifacts(
6284
6556
  contextDir,
6285
6557
  bundle
@@ -6304,7 +6576,7 @@ async function printHostedExecutionPackBundle(essencePath, namespace, jsonOutput
6304
6576
  console.log(` Sections: ${typedBundle.sections.length}`);
6305
6577
  console.log(` Mutations: ${typedBundle.mutations.length}`);
6306
6578
  if (writeContext) {
6307
- console.log(` Context bundle: ${join27(process.cwd(), ".decantr", "context")}`);
6579
+ console.log(` Context bundle: ${join28(process.cwd(), ".decantr", "context")}`);
6308
6580
  console.log(` Files written: ${writtenContextPaths.length}`);
6309
6581
  }
6310
6582
  console.log("");
@@ -6315,16 +6587,33 @@ async function printHostedExecutionPackBundle(essencePath, namespace, jsonOutput
6315
6587
  console.log(` ${cyan3(route.path)} -> ${pageLabel} [${patterns}]`);
6316
6588
  }
6317
6589
  }
6590
+ function resolvePagePackIdForRoute(essencePath, route) {
6591
+ if (!existsSync27(essencePath)) {
6592
+ throw new Error(`Essence file not found at ${essencePath}`);
6593
+ }
6594
+ const essence = JSON.parse(readFileSync20(essencePath, "utf-8"));
6595
+ if (!isV47(essence)) {
6596
+ throw new Error("Route-based pack resolution requires Essence v4.0.0.");
6597
+ }
6598
+ const target = essence.blueprint.routes?.[route];
6599
+ if (!target) {
6600
+ const known = Object.keys(essence.blueprint.routes ?? {}).sort();
6601
+ throw new Error(
6602
+ `Route "${route}" was not found in blueprint.routes. Known routes: ${known.join(", ") || "none"}.`
6603
+ );
6604
+ }
6605
+ return target.page;
6606
+ }
6318
6607
  async function printHostedSelectedExecutionPack(packType, id, essencePath, namespace, jsonOutput = false, writeContext = false) {
6319
6608
  const client = getPublicAPIClient();
6320
- const resolvedPath = essencePath ? resolveUserPath(essencePath) : join27(process.cwd(), "decantr.essence.json");
6321
- if (!existsSync26(resolvedPath)) {
6609
+ const resolvedPath = essencePath ? resolveUserPath(essencePath) : join28(process.cwd(), "decantr.essence.json");
6610
+ if (!existsSync27(resolvedPath)) {
6322
6611
  throw new Error(`Essence file not found at ${resolvedPath}`);
6323
6612
  }
6324
6613
  if ((packType === "section" || packType === "page" || packType === "mutation") && !id) {
6325
6614
  throw new Error(`Pack type "${packType}" requires an id.`);
6326
6615
  }
6327
- const essence = JSON.parse(readFileSync19(resolvedPath, "utf-8"));
6616
+ const essence = JSON.parse(readFileSync20(resolvedPath, "utf-8"));
6328
6617
  const selected = await client.selectExecutionPack(
6329
6618
  {
6330
6619
  essence,
@@ -6335,17 +6624,17 @@ async function printHostedSelectedExecutionPack(packType, id, essencePath, names
6335
6624
  );
6336
6625
  let writtenContextDir = null;
6337
6626
  if (writeContext) {
6338
- const contextDir = join27(process.cwd(), ".decantr", "context");
6339
- mkdirSync12(contextDir, { recursive: true });
6340
- writeFileSync15(
6341
- join27(contextDir, "pack-manifest.json"),
6627
+ const contextDir = join28(process.cwd(), ".decantr", "context");
6628
+ mkdirSync13(contextDir, { recursive: true });
6629
+ writeFileSync16(
6630
+ join28(contextDir, "pack-manifest.json"),
6342
6631
  JSON.stringify(selected.manifest, null, 2) + "\n"
6343
6632
  );
6344
6633
  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);
6345
6634
  const markdownFile = manifestEntry?.markdown ?? `${selected.selector.packType}${selected.selector.id ? `-${selected.selector.id}` : ""}-pack.md`;
6346
6635
  const jsonFile = manifestEntry?.json ?? `${selected.selector.packType}${selected.selector.id ? `-${selected.selector.id}` : ""}-pack.json`;
6347
- writeFileSync15(join27(contextDir, markdownFile), selected.pack.renderedMarkdown);
6348
- writeFileSync15(join27(contextDir, jsonFile), JSON.stringify(selected.pack, null, 2) + "\n");
6636
+ writeFileSync16(join28(contextDir, markdownFile), selected.pack.renderedMarkdown);
6637
+ writeFileSync16(join28(contextDir, jsonFile), JSON.stringify(selected.pack, null, 2) + "\n");
6349
6638
  writtenContextDir = contextDir;
6350
6639
  }
6351
6640
  if (jsonOutput) {
@@ -6370,20 +6659,20 @@ async function printHostedSelectedExecutionPack(packType, id, essencePath, names
6370
6659
  }
6371
6660
  async function printHostedExecutionPackManifest(essencePath, namespace, jsonOutput = false, writeContext = false) {
6372
6661
  const client = getPublicAPIClient();
6373
- const resolvedPath = essencePath ? resolveUserPath(essencePath) : join27(process.cwd(), "decantr.essence.json");
6374
- if (!existsSync26(resolvedPath)) {
6662
+ const resolvedPath = essencePath ? resolveUserPath(essencePath) : join28(process.cwd(), "decantr.essence.json");
6663
+ if (!existsSync27(resolvedPath)) {
6375
6664
  throw new Error(`Essence file not found at ${resolvedPath}`);
6376
6665
  }
6377
- const essence = JSON.parse(readFileSync19(resolvedPath, "utf-8"));
6666
+ const essence = JSON.parse(readFileSync20(resolvedPath, "utf-8"));
6378
6667
  const manifest = await client.getExecutionPackManifest(
6379
6668
  essence,
6380
6669
  namespace ? { namespace } : void 0
6381
6670
  );
6382
6671
  let writtenContextDir = null;
6383
6672
  if (writeContext) {
6384
- const contextDir = join27(process.cwd(), ".decantr", "context");
6385
- mkdirSync12(contextDir, { recursive: true });
6386
- writeFileSync15(join27(contextDir, "pack-manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
6673
+ const contextDir = join28(process.cwd(), ".decantr", "context");
6674
+ mkdirSync13(contextDir, { recursive: true });
6675
+ writeFileSync16(join28(contextDir, "pack-manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
6387
6676
  writtenContextDir = contextDir;
6388
6677
  }
6389
6678
  if (jsonOutput) {
@@ -6404,14 +6693,14 @@ async function printHostedExecutionPackManifest(essencePath, namespace, jsonOutp
6404
6693
  }
6405
6694
  }
6406
6695
  async function hydrateHostedExecutionPacksIfMissing(projectRoot, namespace = "@official") {
6407
- const contextDir = join27(projectRoot, ".decantr", "context");
6408
- const reviewPackPath = join27(contextDir, "review-pack.json");
6409
- const manifestPath = join27(contextDir, "pack-manifest.json");
6410
- if (existsSync26(reviewPackPath) && existsSync26(manifestPath)) {
6696
+ const contextDir = join28(projectRoot, ".decantr", "context");
6697
+ const reviewPackPath = join28(contextDir, "review-pack.json");
6698
+ const manifestPath = join28(contextDir, "pack-manifest.json");
6699
+ if (existsSync27(reviewPackPath) && existsSync27(manifestPath)) {
6411
6700
  return { attempted: false, hydrated: false };
6412
6701
  }
6413
- const essencePath = join27(projectRoot, "decantr.essence.json");
6414
- if (!existsSync26(essencePath)) {
6702
+ const essencePath = join28(projectRoot, "decantr.essence.json");
6703
+ if (!existsSync27(essencePath)) {
6415
6704
  return { attempted: false, hydrated: false };
6416
6705
  }
6417
6706
  const reviewHydration = await hydrateHostedReviewPackIfMissing(projectRoot, namespace);
@@ -6420,9 +6709,9 @@ async function hydrateHostedExecutionPacksIfMissing(projectRoot, namespace = "@o
6420
6709
  }
6421
6710
  try {
6422
6711
  const client = getPublicAPIClient();
6423
- const essence = JSON.parse(readFileSync19(essencePath, "utf-8"));
6712
+ const essence = JSON.parse(readFileSync20(essencePath, "utf-8"));
6424
6713
  const bundle = await client.compileExecutionPacks(essence, { namespace });
6425
- mkdirSync12(contextDir, { recursive: true });
6714
+ mkdirSync13(contextDir, { recursive: true });
6426
6715
  writeExecutionPackBundleArtifacts(contextDir, bundle);
6427
6716
  return { attempted: true, hydrated: true, scope: "bundle" };
6428
6717
  } catch {
@@ -6430,19 +6719,19 @@ async function hydrateHostedExecutionPacksIfMissing(projectRoot, namespace = "@o
6430
6719
  }
6431
6720
  }
6432
6721
  async function hydrateHostedReviewPackIfMissing(projectRoot, namespace = "@official") {
6433
- const contextDir = join27(projectRoot, ".decantr", "context");
6434
- const reviewPackPath = join27(contextDir, "review-pack.json");
6435
- const manifestPath = join27(contextDir, "pack-manifest.json");
6436
- if (existsSync26(reviewPackPath) && existsSync26(manifestPath)) {
6722
+ const contextDir = join28(projectRoot, ".decantr", "context");
6723
+ const reviewPackPath = join28(contextDir, "review-pack.json");
6724
+ const manifestPath = join28(contextDir, "pack-manifest.json");
6725
+ if (existsSync27(reviewPackPath) && existsSync27(manifestPath)) {
6437
6726
  return { attempted: false, hydrated: false };
6438
6727
  }
6439
- const essencePath = join27(projectRoot, "decantr.essence.json");
6440
- if (!existsSync26(essencePath)) {
6728
+ const essencePath = join28(projectRoot, "decantr.essence.json");
6729
+ if (!existsSync27(essencePath)) {
6441
6730
  return { attempted: false, hydrated: false };
6442
6731
  }
6443
6732
  try {
6444
6733
  const client = getPublicAPIClient();
6445
- const essence = JSON.parse(readFileSync19(essencePath, "utf-8"));
6734
+ const essence = JSON.parse(readFileSync20(essencePath, "utf-8"));
6446
6735
  const selected = await client.selectExecutionPack(
6447
6736
  {
6448
6737
  essence,
@@ -6450,14 +6739,14 @@ async function hydrateHostedReviewPackIfMissing(projectRoot, namespace = "@offic
6450
6739
  },
6451
6740
  { namespace }
6452
6741
  );
6453
- mkdirSync12(contextDir, { recursive: true });
6454
- writeFileSync15(join27(contextDir, "review-pack.md"), selected.pack.renderedMarkdown);
6455
- writeFileSync15(
6456
- join27(contextDir, "review-pack.json"),
6742
+ mkdirSync13(contextDir, { recursive: true });
6743
+ writeFileSync16(join28(contextDir, "review-pack.md"), selected.pack.renderedMarkdown);
6744
+ writeFileSync16(
6745
+ join28(contextDir, "review-pack.json"),
6457
6746
  JSON.stringify(selected.pack, null, 2) + "\n"
6458
6747
  );
6459
- if (!existsSync26(manifestPath)) {
6460
- writeFileSync15(manifestPath, JSON.stringify(selected.manifest, null, 2) + "\n");
6748
+ if (!existsSync27(manifestPath)) {
6749
+ writeFileSync16(manifestPath, JSON.stringify(selected.manifest, null, 2) + "\n");
6461
6750
  }
6462
6751
  return { attempted: true, hydrated: true, scope: "review" };
6463
6752
  } catch {
@@ -6467,17 +6756,17 @@ async function hydrateHostedReviewPackIfMissing(projectRoot, namespace = "@offic
6467
6756
  async function printHostedFileCritique(sourcePath, namespace, jsonOutput = false, essencePath, treatmentsPath) {
6468
6757
  const client = getPublicAPIClient();
6469
6758
  const resolvedSourcePath = resolveUserPath(sourcePath);
6470
- const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) : join27(process.cwd(), "decantr.essence.json");
6471
- const resolvedTreatmentsPath = treatmentsPath ? resolveUserPath(treatmentsPath) : join27(process.cwd(), "src", "styles", "treatments.css");
6472
- if (!existsSync26(resolvedSourcePath)) {
6759
+ const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) : join28(process.cwd(), "decantr.essence.json");
6760
+ const resolvedTreatmentsPath = treatmentsPath ? resolveUserPath(treatmentsPath) : join28(process.cwd(), "src", "styles", "treatments.css");
6761
+ if (!existsSync27(resolvedSourcePath)) {
6473
6762
  throw new Error(`Source file not found at ${resolvedSourcePath}`);
6474
6763
  }
6475
- if (!existsSync26(resolvedEssencePath)) {
6764
+ if (!existsSync27(resolvedEssencePath)) {
6476
6765
  throw new Error(`Essence file not found at ${resolvedEssencePath}`);
6477
6766
  }
6478
- const code = readFileSync19(resolvedSourcePath, "utf-8");
6479
- const essence = JSON.parse(readFileSync19(resolvedEssencePath, "utf-8"));
6480
- const treatmentsCss = existsSync26(resolvedTreatmentsPath) ? readFileSync19(resolvedTreatmentsPath, "utf-8") : void 0;
6767
+ const code = readFileSync20(resolvedSourcePath, "utf-8");
6768
+ const essence = JSON.parse(readFileSync20(resolvedEssencePath, "utf-8"));
6769
+ const treatmentsCss = existsSync27(resolvedTreatmentsPath) ? readFileSync20(resolvedTreatmentsPath, "utf-8") : void 0;
6481
6770
  const report = await client.critiqueFile(
6482
6771
  {
6483
6772
  essence,
@@ -6501,11 +6790,11 @@ async function printHostedFileCritique(sourcePath, namespace, jsonOutput = false
6501
6790
  }
6502
6791
  async function printHostedProjectAudit(namespace, jsonOutput = false, essencePath, distPath, sourcesPath) {
6503
6792
  const client = getPublicAPIClient();
6504
- const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) : join27(process.cwd(), "decantr.essence.json");
6505
- if (!existsSync26(resolvedEssencePath)) {
6793
+ const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) : join28(process.cwd(), "decantr.essence.json");
6794
+ if (!existsSync27(resolvedEssencePath)) {
6506
6795
  throw new Error(`Essence file not found at ${resolvedEssencePath}`);
6507
6796
  }
6508
- const essence = JSON.parse(readFileSync19(resolvedEssencePath, "utf-8"));
6797
+ const essence = JSON.parse(readFileSync20(resolvedEssencePath, "utf-8"));
6509
6798
  const dist = readHostedDistSnapshot(distPath);
6510
6799
  const sources = readHostedSourceSnapshot(sourcesPath);
6511
6800
  const report = await client.auditProject(
@@ -6523,7 +6812,7 @@ async function printHostedProjectAudit(namespace, jsonOutput = false, essencePat
6523
6812
  console.log(heading2("Hosted Project Audit"));
6524
6813
  console.log(` Essence: ${resolvedEssencePath}`);
6525
6814
  console.log(
6526
- ` Dist snapshot: ${dist ? distPath ? resolveUserPath(distPath) : join27(process.cwd(), "dist") : "none"}`
6815
+ ` Dist snapshot: ${dist ? distPath ? resolveUserPath(distPath) : join28(process.cwd(), "dist") : "none"}`
6527
6816
  );
6528
6817
  console.log(
6529
6818
  ` Source snapshot: ${sources && sourcesPath ? resolveUserPath(sourcesPath) : "none"}`
@@ -6542,6 +6831,11 @@ function formatBlueprintPortfolioSummary(value) {
6542
6831
  const alternative = portfolio.recommended_alternative ? `; recommended alternative: ${portfolio.recommended_alternative}` : "";
6543
6832
  return `Blueprint set: ${labels.join(" + ")}${alternative}`;
6544
6833
  }
6834
+ function formatRegistryListIdentifier(item) {
6835
+ if (!item || typeof item !== "object") return String(item ?? "");
6836
+ const record = item;
6837
+ return typeof record.slug === "string" && record.slug || typeof record.id === "string" && record.id || typeof record.name === "string" && record.name || "";
6838
+ }
6545
6839
  function printBlueprintPortfolioNotice(blueprint) {
6546
6840
  const portfolio = getBlueprintPortfolioMetadata(blueprint);
6547
6841
  if (!portfolio) return;
@@ -6600,42 +6894,151 @@ async function cmdSearch(query, type, sort, recommended, intelligenceSource, blu
6600
6894
  console.log(dim3(`Search failed. API may be unavailable.`));
6601
6895
  }
6602
6896
  }
6603
- async function cmdSuggest(query, type) {
6604
- const apiClient = getAPIClient();
6605
- const searchType = type || "pattern";
6606
- try {
6607
- const response = await apiClient.search({ q: query, type: searchType });
6608
- const results = response.results;
6609
- if (results.length === 0) {
6610
- console.log(dim3(`No suggestions for "${query}"`));
6611
- console.log("");
6612
- console.log("Try:");
6613
- console.log(` ${cyan3("decantr list patterns")} - see all patterns`);
6614
- console.log(` ${cyan3("decantr search <broader-term>")} - broaden your search`);
6615
- return;
6897
+ function isRecord(value) {
6898
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6899
+ }
6900
+ function patternCandidateFromRegistryItem(item, source) {
6901
+ const record = item;
6902
+ const data = isRecord(record.data) ? record.data : record;
6903
+ const slug = typeof record.slug === "string" && record.slug || typeof data.slug === "string" && data.slug || typeof data.id === "string" && data.id || typeof record.id === "string" && record.id || "pattern";
6904
+ return patternToDiscoveryCandidate(
6905
+ {
6906
+ ...data,
6907
+ id: typeof data.id === "string" ? data.id : slug,
6908
+ slug,
6909
+ name: typeof data.name === "string" ? data.name : typeof record.name === "string" ? record.name : slug,
6910
+ description: typeof data.description === "string" ? data.description : typeof record.description === "string" ? record.description : void 0
6911
+ },
6912
+ { source, slug }
6913
+ );
6914
+ }
6915
+ function readSuggestCodeContext(route, file) {
6916
+ const pieces = [];
6917
+ if (file) {
6918
+ const resolved = isAbsolute(file) ? file : join28(process.cwd(), file);
6919
+ if (existsSync27(resolved)) {
6920
+ pieces.push(readFileSync20(resolved, "utf-8"));
6616
6921
  }
6617
- console.log(heading2(`Suggestions for "${query}"`));
6618
- const queryLower = query.toLowerCase();
6619
- const exact = results.filter((r) => r.slug.toLowerCase().includes(queryLower));
6620
- const related = results.filter((r) => !r.slug.toLowerCase().includes(queryLower));
6621
- if (exact.length > 0) {
6622
- console.log(`${BOLD7}Direct matches:${RESET14}`);
6623
- for (const r of exact.slice(0, 3)) {
6624
- console.log(` ${cyan3(r.slug)} - ${r.description || ""}`);
6922
+ }
6923
+ if (route) {
6924
+ const analysisPath = join28(process.cwd(), ".decantr", "analysis.json");
6925
+ if (existsSync27(analysisPath)) {
6926
+ try {
6927
+ const analysis = JSON.parse(readFileSync20(analysisPath, "utf-8"));
6928
+ const routeEntry = analysis.routes?.routes?.find((entry) => entry.path === route);
6929
+ if (routeEntry?.file) {
6930
+ const resolved = join28(process.cwd(), routeEntry.file);
6931
+ if (existsSync27(resolved)) {
6932
+ pieces.push(readFileSync20(resolved, "utf-8"));
6933
+ }
6934
+ }
6935
+ } catch {
6625
6936
  }
6626
- console.log("");
6627
6937
  }
6628
- if (related.length > 0) {
6629
- console.log(`${BOLD7}Related:${RESET14}`);
6630
- for (const r of related.slice(0, 5)) {
6631
- console.log(` ${cyan3(r.slug)} - ${r.description || ""}`);
6632
- }
6633
- console.log("");
6938
+ }
6939
+ return pieces.join("\n\n").slice(0, 2e4);
6940
+ }
6941
+ async function loadPatternDiscoveryCandidates(registryClient) {
6942
+ const candidates = [];
6943
+ const seen = /* @__PURE__ */ new Set();
6944
+ const add = (candidate) => {
6945
+ const key = candidate.slug || candidate.id;
6946
+ if (seen.has(key)) return;
6947
+ seen.add(key);
6948
+ candidates.push(candidate);
6949
+ };
6950
+ const bundledPatterns = loadBundledContentList("patterns");
6951
+ for (const entry of bundledPatterns) {
6952
+ add(patternToDiscoveryCandidate(entry.data, { source: "bundled", slug: entry.id }));
6953
+ }
6954
+ try {
6955
+ const result = await registryClient.fetchContentList("patterns");
6956
+ for (const item of result.data.items) {
6957
+ const source = result.source.type === "api" ? "hosted" : result.source.type;
6958
+ add(patternCandidateFromRegistryItem(item, source));
6634
6959
  }
6635
- console.log(dim3(`Use "decantr get pattern <id>" for full details`));
6636
6960
  } catch {
6637
- console.log(dim3(`Suggestion search failed. API may be unavailable.`));
6638
6961
  }
6962
+ for (const item of registryClient.listCustomContent("patterns")) {
6963
+ add(patternCandidateFromRegistryItem(item, "custom"));
6964
+ }
6965
+ return candidates;
6966
+ }
6967
+ async function cmdSuggest(query, options = {}) {
6968
+ const searchType = options.type || "pattern";
6969
+ if (searchType !== "pattern" && searchType !== "patterns") {
6970
+ const apiClient = getAPIClient();
6971
+ try {
6972
+ const response = await apiClient.search({ q: query, type: searchType });
6973
+ const results = response.results;
6974
+ if (results.length === 0) {
6975
+ console.log(dim3(`No suggestions for "${query}"`));
6976
+ return;
6977
+ }
6978
+ console.log(heading2(`Suggestions for "${query}"`));
6979
+ for (const r of results.slice(0, 8)) {
6980
+ console.log(` ${cyan3(r.slug)} - ${r.description || r.name || ""}`);
6981
+ }
6982
+ return;
6983
+ } catch {
6984
+ console.log(dim3(`Suggestion search failed. API may be unavailable.`));
6985
+ return;
6986
+ }
6987
+ }
6988
+ const registryClient = new RegistryClient({
6989
+ cacheDir: join28(process.cwd(), ".decantr", "cache")
6990
+ });
6991
+ const code = options.fromCode || options.file ? readSuggestCodeContext(options.route, options.file) : "";
6992
+ const candidates = await loadPatternDiscoveryCandidates(registryClient);
6993
+ const matches = rankPatternCandidates(
6994
+ {
6995
+ query,
6996
+ route: options.route,
6997
+ code,
6998
+ limit: 10
6999
+ },
7000
+ candidates
7001
+ );
7002
+ if (matches.length === 0) {
7003
+ console.log(dim3(`No pattern suggestions for "${query}"`));
7004
+ console.log("");
7005
+ console.log("Try:");
7006
+ console.log(` ${cyan3("decantr list patterns")} - browse slug, name, domain, and source`);
7007
+ console.log(
7008
+ ` ${cyan3('decantr suggest "<broader description>" --from-code --route <route>')} - rank from observed code`
7009
+ );
7010
+ return;
7011
+ }
7012
+ const contextBits = [
7013
+ options.route ? `route ${options.route}` : null,
7014
+ options.file ? `file ${options.file}` : null,
7015
+ code ? "code context" : null
7016
+ ].filter((entry) => Boolean(entry));
7017
+ console.log(
7018
+ heading2(
7019
+ `Pattern suggestions for "${query}"${contextBits.length > 0 ? ` (${contextBits.join(", ")})` : ""}`
7020
+ )
7021
+ );
7022
+ for (const match of matches.slice(0, 8)) {
7023
+ const candidate = match.candidate;
7024
+ const slug = candidate.slug || candidate.id;
7025
+ const details = [
7026
+ candidate.name && candidate.name !== slug ? candidate.name : null,
7027
+ candidate.domain || candidate.category || null,
7028
+ candidate.source || null
7029
+ ].filter(Boolean);
7030
+ console.log(
7031
+ ` ${cyan3(slug)} score ${match.score}${details.length > 0 ? ` ${dim3(details.join(" | "))}` : ""}`
7032
+ );
7033
+ if (candidate.description) {
7034
+ console.log(` ${dim3(candidate.description)}`);
7035
+ }
7036
+ if (match.reasons.length > 0) {
7037
+ console.log(` ${dim3(`why: ${match.reasons.slice(0, 2).join("; ")}`)}`);
7038
+ }
7039
+ }
7040
+ console.log("");
7041
+ console.log(dim3('Use "decantr get pattern <slug>" for full details.'));
6639
7042
  }
6640
7043
  async function cmdGet(type, id) {
6641
7044
  if (!isGetContentType(type)) {
@@ -6645,26 +7048,16 @@ async function cmdGet(type, id) {
6645
7048
  }
6646
7049
  const apiType = CONTENT_TYPE_TO_API_CONTENT_TYPE3[type];
6647
7050
  const registryClient = new RegistryClient({
6648
- cacheDir: join27(process.cwd(), ".decantr", "cache")
7051
+ cacheDir: join28(process.cwd(), ".decantr", "cache")
6649
7052
  });
6650
7053
  const result = await registryClient.fetchContentItem(apiType, id);
6651
7054
  if (result) {
6652
7055
  console.log(JSON.stringify(result.data, null, 2));
6653
7056
  return;
6654
7057
  }
6655
- const currentDir = dirname4(fileURLToPath2(import.meta.url));
6656
- const bundledCandidates = [
6657
- join27(currentDir, "bundled", apiType, `${id}.json`),
6658
- // Running from src/
6659
- join27(currentDir, "..", "src", "bundled", apiType, `${id}.json`),
6660
- // Running from dist/
6661
- join27(currentDir, "..", "bundled", apiType, `${id}.json`)
6662
- // Alternative dist layout
6663
- ];
6664
- const bundledPath = bundledCandidates.find((p) => existsSync26(p)) || null;
6665
- if (bundledPath) {
6666
- const data = JSON.parse(readFileSync19(bundledPath, "utf-8"));
6667
- console.log(JSON.stringify(data, null, 2));
7058
+ const bundled = loadBundledContentItem(apiType, id);
7059
+ if (bundled) {
7060
+ console.log(JSON.stringify(bundled.data, null, 2));
6668
7061
  return;
6669
7062
  }
6670
7063
  console.error(error3(`${type} "${id}" not found.`));
@@ -6672,10 +7065,10 @@ async function cmdGet(type, id) {
6672
7065
  return;
6673
7066
  }
6674
7067
  async function cmdValidate(path) {
6675
- const essencePath = path || join27(process.cwd(), "decantr.essence.json");
7068
+ const essencePath = path || join28(process.cwd(), "decantr.essence.json");
6676
7069
  let raw;
6677
7070
  try {
6678
- raw = readFileSync19(essencePath, "utf-8");
7071
+ raw = readFileSync20(essencePath, "utf-8");
6679
7072
  } catch {
6680
7073
  console.error(error3(`Could not read ${essencePath}`));
6681
7074
  process.exitCode = 1;
@@ -6742,7 +7135,7 @@ async function cmdList(type, sort, recommended, intelligenceSource, blueprintSet
6742
7135
  return;
6743
7136
  }
6744
7137
  const registryClient = new RegistryClient({
6745
- cacheDir: join27(process.cwd(), ".decantr", "cache")
7138
+ cacheDir: join28(process.cwd(), ".decantr", "cache")
6746
7139
  });
6747
7140
  const result = await registryClient.fetchContentList(
6748
7141
  type,
@@ -6752,7 +7145,13 @@ async function cmdList(type, sort, recommended, intelligenceSource, blueprintSet
6752
7145
  intelligenceSource,
6753
7146
  blueprintSet
6754
7147
  );
6755
- const items = result.data.items;
7148
+ const bundledPatternItems = type === "patterns" ? loadBundledContentList("patterns").map((entry) => ({
7149
+ ...entry.data,
7150
+ id: entry.data.id || entry.id,
7151
+ slug: entry.data.slug || entry.id,
7152
+ source: "bundled"
7153
+ })) : [];
7154
+ const items = type === "patterns" ? [...bundledPatternItems, ...result.data.items] : result.data.items;
6756
7155
  if (items.length === 0) {
6757
7156
  console.log(dim3(`No ${type} found.`));
6758
7157
  return;
@@ -6779,7 +7178,21 @@ async function cmdList(type, sort, recommended, intelligenceSource, blueprintSet
6779
7178
  } else {
6780
7179
  console.log(heading2(`${items.length} ${type} found`));
6781
7180
  for (const item of items) {
6782
- console.log(` ${cyan3(item.id)} ${dim3(item.description || item.name || "")}`);
7181
+ if (type === "patterns") {
7182
+ const pattern = item;
7183
+ const slug = pattern.slug || pattern.id;
7184
+ const domain = pattern.domain || pattern.category || pattern.tags?.[0] || "general";
7185
+ const source = pattern.source || (result.source.type === "api" ? "hosted" : result.source.type);
7186
+ const label = [pattern.name && pattern.name !== slug ? pattern.name : null, domain, source].filter(Boolean).join(" | ");
7187
+ console.log(` ${cyan3(slug)} ${dim3(label)}`);
7188
+ if (pattern.description) {
7189
+ console.log(` ${dim3(pattern.description)}`);
7190
+ }
7191
+ } else {
7192
+ console.log(
7193
+ ` ${cyan3(formatRegistryListIdentifier(item))} ${dim3(item.description || item.name || "")}`
7194
+ );
7195
+ }
6783
7196
  const intelligenceSummary = formatIntelligenceSummary(
6784
7197
  item.intelligence
6785
7198
  );
@@ -6805,10 +7218,10 @@ ${CYAN8}Telemetry enabled.${RESET14} Decantr will send privacy-filtered CLI prod
6805
7218
  }
6806
7219
  function readCliPackageVersion() {
6807
7220
  const here = dirname4(fileURLToPath2(import.meta.url));
6808
- const candidates = [join27(here, "..", "package.json"), join27(here, "..", "..", "package.json")];
7221
+ const candidates = [join28(here, "..", "package.json"), join28(here, "..", "..", "package.json")];
6809
7222
  for (const candidate of candidates) {
6810
7223
  try {
6811
- const pkg = JSON.parse(readFileSync19(candidate, "utf-8"));
7224
+ const pkg = JSON.parse(readFileSync20(candidate, "utf-8"));
6812
7225
  if (pkg.version) return pkg.version;
6813
7226
  } catch {
6814
7227
  }
@@ -6819,19 +7232,19 @@ function timestampForFile() {
6819
7232
  return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
6820
7233
  }
6821
7234
  function backupExistingEssence(projectRoot, label) {
6822
- const essencePath = join27(projectRoot, "decantr.essence.json");
6823
- if (!existsSync26(essencePath)) return null;
6824
- const backupPath = join27(
7235
+ const essencePath = join28(projectRoot, "decantr.essence.json");
7236
+ if (!existsSync27(essencePath)) return null;
7237
+ const backupPath = join28(
6825
7238
  projectRoot,
6826
7239
  `decantr.essence.${label}.${timestampForFile()}.backup.json`
6827
7240
  );
6828
- writeFileSync15(backupPath, readFileSync19(essencePath, "utf-8"), "utf-8");
7241
+ writeFileSync16(backupPath, readFileSync20(essencePath, "utf-8"), "utf-8");
6829
7242
  return backupPath;
6830
7243
  }
6831
7244
  function writeBrownfieldProjectJson(input) {
6832
- const decantrDir = join27(input.projectRoot, ".decantr");
6833
- mkdirSync12(join27(decantrDir, "context"), { recursive: true });
6834
- mkdirSync12(join27(decantrDir, "cache"), { recursive: true });
7245
+ const decantrDir = join28(input.projectRoot, ".decantr");
7246
+ mkdirSync13(join28(decantrDir, "context"), { recursive: true });
7247
+ mkdirSync13(join28(decantrDir, "cache"), { recursive: true });
6835
7248
  const now = (/* @__PURE__ */ new Date()).toISOString();
6836
7249
  const projectJson = {
6837
7250
  detected: {
@@ -6872,7 +7285,7 @@ function writeBrownfieldProjectJson(input) {
6872
7285
  }
6873
7286
  }
6874
7287
  };
6875
- writeFileSync15(join27(decantrDir, "project.json"), JSON.stringify(projectJson, null, 2) + "\n");
7288
+ writeFileSync16(join28(decantrDir, "project.json"), JSON.stringify(projectJson, null, 2) + "\n");
6876
7289
  }
6877
7290
  async function applyAcceptedBrownfieldProposal(input) {
6878
7291
  const proposal = readBrownfieldProposal(input.projectRoot);
@@ -6888,8 +7301,8 @@ async function applyAcceptedBrownfieldProposal(input) {
6888
7301
  process.exitCode = 1;
6889
7302
  return;
6890
7303
  }
6891
- const essencePath = join27(input.projectRoot, "decantr.essence.json");
6892
- const hasEssence = existsSync26(essencePath);
7304
+ const essencePath = join28(input.projectRoot, "decantr.essence.json");
7305
+ const hasEssence = existsSync27(essencePath);
6893
7306
  let essence;
6894
7307
  let backupPath = null;
6895
7308
  if (input.mode === "accept" && hasEssence) {
@@ -6903,7 +7316,7 @@ async function applyAcceptedBrownfieldProposal(input) {
6903
7316
  return;
6904
7317
  }
6905
7318
  if (input.mode === "merge" && hasEssence) {
6906
- const existing = JSON.parse(readFileSync19(essencePath, "utf-8"));
7319
+ const existing = JSON.parse(readFileSync20(essencePath, "utf-8"));
6907
7320
  if (!isV47(existing)) {
6908
7321
  console.log(
6909
7322
  error3(
@@ -6940,9 +7353,9 @@ async function applyAcceptedBrownfieldProposal(input) {
6940
7353
  assistantBridge: input.assistantBridge,
6941
7354
  mode: input.mode
6942
7355
  });
6943
- writeFileSync15(essencePath, JSON.stringify(essence, null, 2) + "\n", "utf-8");
7356
+ writeFileSync16(essencePath, JSON.stringify(essence, null, 2) + "\n", "utf-8");
6944
7357
  const registryClient = new RegistryClient({
6945
- cacheDir: join27(input.projectRoot, ".decantr", "cache"),
7358
+ cacheDir: join28(input.projectRoot, ".decantr", "cache"),
6946
7359
  offline: true,
6947
7360
  projectRoot: input.projectRoot
6948
7361
  });
@@ -7096,7 +7509,7 @@ async function cmdInit(args) {
7096
7509
  }
7097
7510
  }
7098
7511
  const registryClient = new RegistryClient({
7099
- cacheDir: join27(projectRoot, ".decantr", "cache"),
7512
+ cacheDir: join28(projectRoot, ".decantr", "cache"),
7100
7513
  apiUrl: args.registry,
7101
7514
  offline: args.offline,
7102
7515
  projectRoot
@@ -7426,7 +7839,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET14}`);
7426
7839
  if (appliedRuleFiles.length > 0) {
7427
7840
  console.log(` ${dim3(`Rule bridge applied: ${appliedRuleFiles.join(", ")}`)}`);
7428
7841
  }
7429
- if (!existsSync26(join27(projectRoot, "package.json"))) {
7842
+ if (!existsSync27(join28(projectRoot, "package.json"))) {
7430
7843
  console.log("");
7431
7844
  console.log(
7432
7845
  dim3(` Note: ${cyan3("decantr init")} created Decantr contract/context files only.`)
@@ -7437,7 +7850,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET14}`);
7437
7850
  )
7438
7851
  );
7439
7852
  }
7440
- const hasCompiledPacks = existsSync26(join27(projectRoot, ".decantr", "context", "scaffold-pack.md"));
7853
+ const hasCompiledPacks = existsSync27(join28(projectRoot, ".decantr", "context", "scaffold-pack.md"));
7441
7854
  console.log("");
7442
7855
  console.log(" Next steps:");
7443
7856
  if (hasCompiledPacks) {
@@ -7477,7 +7890,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET14}`);
7477
7890
  console.log(` ${cyan3("decantr upgrade")} Update to latest patterns`);
7478
7891
  console.log(` ${cyan3("decantr check")} Detect drift issues`);
7479
7892
  console.log(` ${cyan3("decantr migrate --to v4")} Migrate older essence files to v4`);
7480
- const essenceContent = readFileSync19(result.essencePath, "utf-8");
7893
+ const essenceContent = readFileSync20(result.essencePath, "utf-8");
7481
7894
  const essence = JSON.parse(essenceContent);
7482
7895
  const validation = validateEssence2(essence);
7483
7896
  if (!validation.valid) {
@@ -7533,16 +7946,16 @@ Validation warnings: ${validation.errors.join(", ")}`));
7533
7946
  }
7534
7947
  async function cmdStatus() {
7535
7948
  const projectRoot = process.cwd();
7536
- const essencePath = join27(projectRoot, "decantr.essence.json");
7537
- const projectJsonPath = join27(projectRoot, ".decantr", "project.json");
7949
+ const essencePath = join28(projectRoot, "decantr.essence.json");
7950
+ const projectJsonPath = join28(projectRoot, ".decantr", "project.json");
7538
7951
  console.log(heading2("Decantr Project Status"));
7539
- if (!existsSync26(essencePath)) {
7952
+ if (!existsSync27(essencePath)) {
7540
7953
  console.log(`${RED11}No decantr.essence.json found.${RESET14}`);
7541
7954
  console.log(dim3('Run "decantr init" to create one.'));
7542
7955
  return;
7543
7956
  }
7544
7957
  try {
7545
- const essence = JSON.parse(readFileSync19(essencePath, "utf-8"));
7958
+ const essence = JSON.parse(readFileSync20(essencePath, "utf-8"));
7546
7959
  const validation = validateEssence2(essence);
7547
7960
  const essenceVersion = isV47(essence) ? "v4" : "legacy";
7548
7961
  console.log(`${BOLD7}Essence:${RESET14}`);
@@ -7590,9 +8003,9 @@ async function cmdStatus() {
7590
8003
  }
7591
8004
  console.log("");
7592
8005
  console.log(`${BOLD7}Sync Status:${RESET14}`);
7593
- if (existsSync26(projectJsonPath)) {
8006
+ if (existsSync27(projectJsonPath)) {
7594
8007
  try {
7595
- const projectJson = JSON.parse(readFileSync19(projectJsonPath, "utf-8"));
8008
+ const projectJson = JSON.parse(readFileSync20(projectJsonPath, "utf-8"));
7596
8009
  const syncStatus = projectJson.sync?.status || "unknown";
7597
8010
  const lastSync = projectJson.sync?.lastSync || "never";
7598
8011
  const source = projectJson.sync?.registrySource || "unknown";
@@ -7610,7 +8023,7 @@ async function cmdStatus() {
7610
8023
  }
7611
8024
  async function cmdSync() {
7612
8025
  const projectRoot = process.cwd();
7613
- const cacheDir = join27(projectRoot, ".decantr", "cache");
8026
+ const cacheDir = join28(projectRoot, ".decantr", "cache");
7614
8027
  console.log(heading2("Syncing registry content..."));
7615
8028
  const result = await syncRegistry(cacheDir);
7616
8029
  if (result.synced.length > 0) {
@@ -7805,14 +8218,14 @@ ${BOLD7}Examples:${RESET14}
7805
8218
  process.exitCode = 1;
7806
8219
  return;
7807
8220
  }
7808
- const themePath = join27(projectRoot, ".decantr", "custom", "themes", `${name}.json`);
7809
- if (!existsSync26(themePath)) {
8221
+ const themePath = join28(projectRoot, ".decantr", "custom", "themes", `${name}.json`);
8222
+ if (!existsSync27(themePath)) {
7810
8223
  console.error(error3(`Theme "${name}" not found at ${themePath}`));
7811
8224
  process.exitCode = 1;
7812
8225
  return;
7813
8226
  }
7814
8227
  try {
7815
- const theme = JSON.parse(readFileSync19(themePath, "utf-8"));
8228
+ const theme = JSON.parse(readFileSync20(themePath, "utf-8"));
7816
8229
  const result = validateCustomTheme(theme);
7817
8230
  if (result.valid) {
7818
8231
  console.log(success3(`Custom theme "${name}" is valid`));
@@ -7896,7 +8309,7 @@ ${BOLD7}Usage:${RESET14}
7896
8309
  decantr check --brownfield
7897
8310
  decantr sync-drift
7898
8311
  decantr search <query> [--type <type>] [--sort <recommended|recent|name>] [--recommended] [--source <authored|benchmark|hybrid>]
7899
- decantr suggest <query> [--type <type>]
8312
+ decantr suggest <query> [--type <type>] [--route <route>] [--file <path>] [--from-code]
7900
8313
  decantr get <type> <id>
7901
8314
  decantr list <type> [--sort <recommended|recent|name>] [--recommended] [--source <authored|benchmark|hybrid>]
7902
8315
  decantr showcase [manifest|shortlist|verification] [--json]
@@ -7906,7 +8319,8 @@ ${BOLD7}Usage:${RESET14}
7906
8319
  decantr registry critique-file <file> [--namespace <namespace>] [--json] [--essence <path>] [--treatments <path>]
7907
8320
  decantr registry audit-project [--namespace <namespace>] [--json] [--essence <path>] [--dist <path>] [--sources <dir>]
7908
8321
  decantr health [--format text|json|markdown] [--ci] [--fail-on error|warn|none]
7909
- decantr health --evidence [--browser] [--design-tokens <path>]
8322
+ decantr health --evidence [--browser] [--base-url <url>] [--design-tokens <path>]
8323
+ decantr health --save-baseline | --since-baseline
7910
8324
  decantr health init-ci [--force] [--project <path>] [--workspace] [--fail-on <error|warn|none>] [--cli-version <version|latest>]
7911
8325
  decantr workspace list [--json]
7912
8326
  decantr workspace health [--json] [--changed --since origin/main]
@@ -8013,7 +8427,7 @@ ${BOLD7}Examples:${RESET14}
8013
8427
  decantr check --brownfield
8014
8428
  decantr sync-drift
8015
8429
  decantr search dashboard
8016
- decantr suggest leaderboard
8430
+ decantr suggest "recipe feed with infinite scroll" --route /feed --from-code
8017
8431
  decantr list patterns
8018
8432
  decantr showcase shortlist
8019
8433
  decantr showcase verification --json
@@ -8030,7 +8444,7 @@ ${BOLD7}Examples:${RESET14}
8030
8444
  ${BOLD7}Workflow Model:${RESET14}
8031
8445
  ${cyan3("Greenfield blueprint")} decantr new my-app --blueprint=X --workflow=greenfield --adoption=decantr-css
8032
8446
  ${cyan3("Greenfield contract")} decantr init --workflow=greenfield --adoption=contract-only
8033
- ${cyan3("Brownfield adoption")} decantr analyze -> decantr init --existing --accept-proposal -> decantr check --brownfield
8447
+ ${cyan3("Brownfield adoption")} decantr analyze -> decantr init --existing --accept-proposal -> decantr health --browser --evidence
8034
8448
  ${cyan3("Hybrid composition")} decantr add/remove, decantr theme switch, decantr registry, decantr upgrade
8035
8449
 
8036
8450
  ${BOLD7}Bootstrap adapters:${RESET14}
@@ -8070,6 +8484,9 @@ ${BOLD7}Usage:${RESET14}
8070
8484
  decantr health --ci [--fail-on error|warn|none]
8071
8485
  decantr health --prompt <finding-id>
8072
8486
  decantr health --evidence [--browser] [--design-tokens <path>]
8487
+ decantr health --browser --base-url <url> --evidence
8488
+ decantr health --save-baseline
8489
+ decantr health --since-baseline
8073
8490
  decantr health init-ci [--force] [--project <path>] [--workspace] [--fail-on error|warn|none] [--cli-version <version|latest>]
8074
8491
 
8075
8492
  ${BOLD7}Options:${RESET14}
@@ -8082,6 +8499,9 @@ ${BOLD7}Options:${RESET14}
8082
8499
  --prompt Print an AI-ready remediation prompt for a finding
8083
8500
  --evidence Emit a local Evidence Bundle JSON artifact
8084
8501
  --browser Include optional rendered-browser setup/evidence checks
8502
+ --base-url Base URL for rendered route checks when --browser is enabled
8503
+ --save-baseline Save the current health state for later comparison
8504
+ --since-baseline Compare this run to .decantr/health-baseline.json
8085
8505
  --design-tokens Compare against a Figma/Tokens Studio JSON export
8086
8506
 
8087
8507
  ${BOLD7}Examples:${RESET14}
@@ -8165,6 +8585,61 @@ ${BOLD7}Examples:${RESET14}
8165
8585
  decantr studio --workspace
8166
8586
  `);
8167
8587
  }
8588
+ function cmdRegistryHelp() {
8589
+ console.log(`
8590
+ ${BOLD7}decantr registry${RESET14} \u2014 Read hosted execution packs and registry intelligence
8591
+
8592
+ ${BOLD7}Usage:${RESET14}
8593
+ decantr registry summary [--namespace <namespace>] [--json]
8594
+ decantr registry compile-packs [path] [--namespace <namespace>] [--json] [--write-context]
8595
+ decantr registry get-pack <manifest|scaffold|review|section|page|mutation> [id] [--namespace <namespace>] [--json] [--essence <path>] [--write-context]
8596
+ decantr registry get-pack page --route <route> [--namespace <namespace>] [--json] [--essence <path>]
8597
+ decantr registry critique-file <file> [--namespace <namespace>] [--json] [--essence <path>] [--treatments <path>]
8598
+ decantr registry audit-project [--namespace <namespace>] [--json] [--essence <path>] [--dist <path>] [--sources <dir>]
8599
+ `);
8600
+ }
8601
+ function cmdThemeHelp() {
8602
+ console.log(`
8603
+ ${BOLD7}decantr theme${RESET14} \u2014 Manage custom themes
8604
+
8605
+ ${BOLD7}Usage:${RESET14}
8606
+ decantr theme create <name>
8607
+ decantr theme create <name> --guided
8608
+ decantr theme list
8609
+ decantr theme validate <name>
8610
+ decantr theme delete <name>
8611
+ decantr theme import <path>
8612
+ `);
8613
+ }
8614
+ function printCommandHelp(command, args) {
8615
+ if (!isCommandHelpRequest(args)) return false;
8616
+ switch (command) {
8617
+ case "health":
8618
+ cmdHealthHelp();
8619
+ return true;
8620
+ case "content-health":
8621
+ cmdContentHealthHelp();
8622
+ return true;
8623
+ case "studio":
8624
+ cmdStudioHelp();
8625
+ return true;
8626
+ case "workspace":
8627
+ cmdWorkspaceHelp();
8628
+ return true;
8629
+ case "rules":
8630
+ cmdRulesHelp();
8631
+ return true;
8632
+ case "registry":
8633
+ cmdRegistryHelp();
8634
+ return true;
8635
+ case "theme":
8636
+ cmdThemeHelp();
8637
+ return true;
8638
+ default:
8639
+ cmdHelp();
8640
+ return true;
8641
+ }
8642
+ }
8168
8643
  async function main() {
8169
8644
  const args = process.argv.slice(2);
8170
8645
  const command = args[0];
@@ -8175,10 +8650,10 @@ async function main() {
8175
8650
  if (command === "--version" || command === "-v" || command === "version") {
8176
8651
  try {
8177
8652
  const here = dirname4(fileURLToPath2(import.meta.url));
8178
- const candidates = [join27(here, "..", "package.json"), join27(here, "..", "..", "package.json")];
8653
+ const candidates = [join28(here, "..", "package.json"), join28(here, "..", "..", "package.json")];
8179
8654
  for (const candidate of candidates) {
8180
- if (existsSync26(candidate)) {
8181
- const pkg = JSON.parse(readFileSync19(candidate, "utf-8"));
8655
+ if (existsSync27(candidate)) {
8656
+ const pkg = JSON.parse(readFileSync20(candidate, "utf-8"));
8182
8657
  if (pkg.version) {
8183
8658
  console.log(pkg.version);
8184
8659
  return;
@@ -8193,6 +8668,9 @@ async function main() {
8193
8668
  }
8194
8669
  return;
8195
8670
  }
8671
+ if (printCommandHelp(command, args)) {
8672
+ return;
8673
+ }
8196
8674
  switch (command) {
8197
8675
  case "new": {
8198
8676
  const newName = args[1];
@@ -8290,7 +8768,7 @@ async function main() {
8290
8768
  `${YELLOW9}Note: \`decantr heal\` is deprecated. Use \`decantr check\` instead.${RESET14}`
8291
8769
  );
8292
8770
  }
8293
- const { cmdHeal } = await import("./heal-M6PRCIIF.js");
8771
+ const { cmdHeal } = await import("./heal-ZYD6NVGE.js");
8294
8772
  const telemetryFlag = args.includes("--telemetry");
8295
8773
  const brownfieldFlag = args.includes("--brownfield");
8296
8774
  await cmdHeal(process.cwd(), { telemetry: telemetryFlag, brownfield: brownfieldFlag });
@@ -8302,7 +8780,7 @@ async function main() {
8302
8780
  cmdHealthHelp();
8303
8781
  break;
8304
8782
  }
8305
- const { cmdHealth, parseHealthArgs } = await import("./health-ZXOPGNBZ.js");
8783
+ const { cmdHealth, parseHealthArgs } = await import("./health-ETZXWGTW.js");
8306
8784
  await cmdHealth(process.cwd(), parseHealthArgs(args));
8307
8785
  } catch (e) {
8308
8786
  console.error(error3(e.message));
@@ -8330,7 +8808,7 @@ async function main() {
8330
8808
  cmdStudioHelp();
8331
8809
  break;
8332
8810
  }
8333
- const { cmdStudio, parseStudioArgs } = await import("./studio-LHQXHBE7.js");
8811
+ const { cmdStudio, parseStudioArgs } = await import("./studio-MKLBUC3A.js");
8334
8812
  await cmdStudio(process.cwd(), parseStudioArgs(args));
8335
8813
  } catch (e) {
8336
8814
  console.error(error3(e.message));
@@ -8344,7 +8822,7 @@ async function main() {
8344
8822
  cmdWorkspaceHelp();
8345
8823
  break;
8346
8824
  }
8347
- const { cmdWorkspace } = await import("./workspace-MOLAGT2B.js");
8825
+ const { cmdWorkspace } = await import("./workspace-KSFWRZEX.js");
8348
8826
  await cmdWorkspace(process.cwd(), args);
8349
8827
  } catch (e) {
8350
8828
  console.error(error3(e.message));
@@ -8427,13 +8905,22 @@ async function main() {
8427
8905
  case "suggest": {
8428
8906
  const query = args[1];
8429
8907
  if (!query) {
8430
- console.error(error3("Usage: decantr suggest <query> [--type <type>]"));
8908
+ console.error(
8909
+ error3(
8910
+ "Usage: decantr suggest <query> [--type <type>] [--route <route>] [--file <path>] [--from-code]"
8911
+ )
8912
+ );
8431
8913
  process.exitCode = 1;
8432
8914
  return;
8433
8915
  }
8434
8916
  const typeIdx = args.indexOf("--type");
8435
8917
  const type = typeIdx !== -1 ? args[typeIdx + 1] : void 0;
8436
- await cmdSuggest(query, type);
8918
+ const routeIdx = args.indexOf("--route");
8919
+ const route = routeIdx !== -1 ? args[routeIdx + 1] : void 0;
8920
+ const fileIdx = args.indexOf("--file");
8921
+ const file = fileIdx !== -1 ? args[fileIdx + 1] : void 0;
8922
+ const fromCode = args.includes("--from-code");
8923
+ await cmdSuggest(query, { type, route, file, fromCode });
8437
8924
  break;
8438
8925
  }
8439
8926
  case "get": {
@@ -8600,7 +9087,9 @@ async function main() {
8600
9087
  const essenceIdx = args.indexOf("--essence");
8601
9088
  const essencePath = essenceIdx !== -1 ? args[essenceIdx + 1] : void 0;
8602
9089
  const packType = args[2] && !args[2].startsWith("--") ? args[2] : void 0;
8603
- const id = args[3] && !args[3].startsWith("--") ? args[3] : void 0;
9090
+ const routeIdx = args.indexOf("--route");
9091
+ const route = routeIdx !== -1 ? args[routeIdx + 1] : void 0;
9092
+ let id = args[3] && !args[3].startsWith("--") ? args[3] : void 0;
8604
9093
  if (!packType || !["manifest", "scaffold", "review", "section", "page", "mutation"].includes(packType)) {
8605
9094
  console.error(
8606
9095
  `${RED11}Usage: decantr registry get-pack <manifest|scaffold|review|section|page|mutation> [id] [--namespace <namespace>] [--json] [--essence <path>] [--write-context]${RESET14}`
@@ -8612,6 +9101,10 @@ async function main() {
8612
9101
  await printHostedExecutionPackManifest(essencePath, namespace, jsonOutput, writeContext);
8613
9102
  break;
8614
9103
  }
9104
+ if (packType === "page" && route && !id) {
9105
+ const resolvedPath = essencePath ? resolveUserPath(essencePath) : join28(process.cwd(), "decantr.essence.json");
9106
+ id = resolvePagePackIdForRoute(resolvedPath, route);
9107
+ }
8615
9108
  await printHostedSelectedExecutionPack(
8616
9109
  packType,
8617
9110
  id,